Today, I was surprised to discover something that I didn’t know about private variables in AS3. If an instance of class A has a reference to another instance of class A, then the second object’s private variables are accessible to the first object. No restrictions. No special tricks. You just use obj.variableName
like any other property request. I’ll show you some example code, and you can try it yourself.
First, let’s set up the situation. Alice has a secret in her diary. Trudy wants to know Alice’s secret, so she’s going to sneak in and take a peek. Here’s a simple document class that presents our story in code:
package { import flash.display.Sprite; public class ISeeYourPrivates extends Sprite { public function ISeeYourPrivates() { //the intruder var trudy:Person = new Person("Trudy"); //the victim var alice:Person = new Person("Alice", "I kissed a girl, and I liked it!"); //Trudy is curious, and she wants to read Alice's diary trudy.peekIntoDiary(alice); } } }
Finally, this UML-like diagram below also shows what’s happening:

Next, let’s look at the Person
class and see how the peekIntoDiary()
method works:
package { public class Person { public function Person(name:String, secret:String = null) { this.name = name; this._secret = secret; } public function peekIntoDiary(target:Person):void { trace(this.name, "is looking at ", target.name + "'s secret:", "\"" + target._secret + "\""); } public var name:String; private var _secret:String; } }
As you can see the secret value sent to the constructor gets stored in the private variable _secret
. Another Person
instance is passed to the peekIntoDiary()
method. In this case, the Trudy object (this
) now has access to the Alice object (target
). The trace()
output reads:
Trudy is looking at Alice’s secret: “I kissed a girl, and I liked it!”
Yes, Alice’s private variable _secret
has just been accessed by Trudy. Bet you didn’t expect that to work. I know I didn’t. Don’t believe me? Try it yourself. The code above compiles and runs.
It makes some sense if you consider that the private
access modifier is actually a namespace. Basically, behind the scenes, the compiler automatically creates a separate namespace for each individual class that it names private. This private namespace is only accessible to the one class for which it is defined. That’s why other classes and subclasses can’t access private variables. However, due to the scope of the namespace, all instances of the same class can access the private members of other instances. To be truly private, each individual instance of a class would needs its own namespace that other instances wouldn’t know about. I’m not even sure if that’s possible, but I have a vague idea of how it might work.
Does it matter? No, not really. You’ll rarely find yourself in an situation where this behavior can be useful (see the credited link below for one possibility). I can’t think of a case where this can be considered a security risk either. It’s not much different than using object["variableName"]
to get at private variables in AS2. Mainly, I wanted to share because it’s somewhat unexpected behavior.
Credit: Thanks to Nick Bilyk for pointing this out in a post about cloning objects.
I wouldn’t say that the behavior is unexpected. As you pointed out private is just another namespace, just one that the compiler has special treatment for. The documentation about access control namespace attributes defines private as “makes a property visible only to callers within the property’s defining class.” The example you have above shows how the peekIntoDiary() function since it is defined in the Person class can access anything defined else defined in the Person class, regardless of the instance.
Hello Josh,
Nice post. I am taking a class called «Concepts of Object-Oriented Programming.» There I found out about this behavior in Java, meaning «private» is class-private, not object-private. I was telling my friend that there’s no way this works the same in ActionScript. I guess I was wrong after all. =)
Cheers,
Daniel
Thanks to the two Daniels for the interesting info. I guess I always just assumed that, using the terms the second Daniel mentioned,
private
means object-private rather than class-private. Like I said, it makes sense from a namespace point-of-view, but this is an assumption I’ve been making since my CS101 class in college (when I was using Java, which I’ve now learned has the same behavior). From my point of view, makingprivate
mean class-private rather than object-private makes objects less encapsulated, so I guess that’s why I made the assumption.Anyway, interesting stuff. I’m sure this knowledge will come in handy at some point in the future.
Actually, this behavior is completely standard and logical. Who decided this variable is private ? The guy that wrote that class. When he writes other methods of this class, he is home. If he can make this variable public, why couldn’t he read it when it’s private ? It’s his !
And to be completely honest, I have to say I also was surprise when I first discovered it 🙂
The OOP makes us see objects as independent individuals, and we tend to forget that OOP security is to protect from coders, not objects.
We’re just getting too virtual, I guess.
I have a question for the commenters who say this isn’t unexpected. If it seems logical to you, then why don’t I see developers taking advantage of this behavior more often? From the moment I started learning software development in 2001 to yesterday, over seven years later, I’d never seen anyone use this feature. Why not? Are there too few use-cases? Is it rare for one instance to have a reference to another instance of the same type? What is it?
It honestly seems very strange to me that I would never have encountered this in code written in any of the languages I’ve used before, whether ActionScript, Java, or something else. I’ve probably seen every other obscure feature of ActionScript used at least once. I’ve even seen undocumented features in AS2 used sometimes. Why not this one?
This behavior is pretty standard across langauges, but it’s definitely a surprise the first time you notice it. I know C++ and Java operate the same way. As others have pointed out, it makes sense and can be useful.
Another case would be for a tree or linked-list object, where you may have code to remove a node from the tree: _parent._child = null; _parent = null;
You don’t see it too often, but it’s definitely out there. Maybe people are just so accustomed to making public accessor methods for their private variables, that they end up using them even in these cases? 🙂
The tree structure is an excellent example.
And, you may not have noticed it because it’s sometime not simple to notice it, especially in Java where you don’t have the get/set types of functions, so no need for “_” before object arguments.
Josh, maybe the question is really what use-cases can you think of where this would be useful? The point of private variables is not to stop bad objects seeing/changing something they shouldn’t, it’s to stop bad coders seeing/changing something they shouldn’t. As Olivier alluded to above, if you have access to the class to put in your peekIntoDiary function, you could just as easily put in a getter function or make the variable public.
The place I see this used most in Java is in implementing a .equals() method. Usually classes which provide their own .equals method will just go through and compare all the member properties of two instances. It would be awkward to implement without access to the other object’s private members.
That being said, I agree with the general sentiment that the behavior is non-obvious at first.
Kinda sneaky 😉
Thanks
A private member variable may be accessed if a method is written for it, but it can not be accessed if no method exists for it. In other words:
var oJane:Person = new Person(‘Jane’, ‘Jane has a secret’);
// will error:
// trace(‘Jane is secure from direct member access: ‘ + oJane._secret);
// will succeed:
trace(‘Jane is not secure by method and design: ‘ + oJane.peekIntoDiary(this));