AS1 OOP: Controlling OOP
by senocular
__constructor__
The tradition of double underscore padded variables
continues with the __constructor__ property. The final of
the 3 ‘automatic’ properties in instances, __constructor__
is a property referencing the constructor of a class
instance primarily for use by the super operator. Basically
its more or less a repeat of the constructor property.
However, its purpose is not to tell you what the constructor
of an instance is, but rather to tell super what the
constructor is. As such, __constructor__ is more of a
prototype object property. All class instances have them,
but those in use by the super operator are those in
prototype objects (where super looks to figure out what your
instance’s class constructor is).
Just as any object instance looks to its own __proto__
for the next shared object in its inheritance chain, super
uses __constructor__ to find an instance’s constructor.
Super, however, doesn’t use the instance’s own
__constructor__ property, it uses the instance’s class
prototype __constructor__ property, or to that instance, its
__proto__.__constructor__. After all, the instance’s
constructor is not the super class constructor. Its still
its own constructor. The instance’s class prototype’s
constructor, however, would be the instances super class
constructor. So it’s there the super must look.
You can think of these double underscore properties as
road signs, pointing in the direction of prototype objects
and constructors.
[ __proto__ and __constructor__ as pointing signs ]
These road signs, however, are for internal use only. So
they’re more along the lines of road signs in a secret
military base. Luckily for us, we know where that base is
and can sneak in during nights to change those signs around.
A quick example of switching those signs, or at least
__constructor__, can be seen with email. Because email has
certain common elements that you would find with any other
type of more general mail, things like a recipient and a
message, it can be made as a subclass of a mail super class.
As a subclass of mail, email would need its instances to
have mail properties defined for them when created, thus
super is utilized to provide that. But what if some sneaky
advertiser came in and changed e-mail's __constructor__
property
- Spam = function(){
- this.to = "everyone in your address book";
- this.message = "ENLARGE YOURSELF BY 200%!";
- };
- Mail = function(recipient, message){
- this.recipient = recipient;
- this.message = message;
- };
- Email = function(subject, recipient, message){
- this.subject = subject;
- super(recipient, message); // uses __constructor__
- };
- Email.prototype = new Mail();
- myMail = new Email("greetings", "you", "hello! your
friend, xxx");
- trace(myMail.subject); // greetings
- trace(myMail.recipient); // you
- trace(myMail.message); // hello! your friend, xxx
- // change email’s __constructor__ and witness the
results
- Email.prototype.__constructor__ = Spam;
- myMail = new Email("greetings", "you", "hello! your
friend, xxx");
- trace(myMail.subject); // greetings
- trace(myMail.recipient); // everyone in your address
book
- trace(myMail.message); // ENLARGE YOURSELF BY 200%!
You can see what happened with super after the
email.prototype.__constructor__ was changed from pointing to
its default of mail to instead the spam function. The super
call no longer saw mail as being the super class of email,
it saw spam as the super class, and as such, the definitions
provided by spam were added into the myMail instance within
the constructor call.
Note that inheritance was established for the prior
example. For classes not inheriting from other classes, you
have no __constructor__ property in the class’s prototype.
Because its an instance property, it’s assigned from the
instance created in establishing inheritance with a new
super class defined as the prototype object. Default
prototype obejcts are without __constructor__ properties
but, they do have constructor properties, and they reference
the class constructor function.
- MyClass = function(){};
- trace(MyClass.prototype.__constructor__); // traces
undefined
- trace(MyClass.prototype.constructor); // traces [type
Function]
- trace(MyClass.prototype.constructor == MyClass); //
traces true
Similarly, the constructor function itself, as a Function
object instance, also is without a __constructor__ property.
- MyClass = function(){}
- trace(MyClass.__constructor__); // traces undefined
- trace(MyClass.constructor); // traces [type Function]
- trace(MyClass.constructor == Function); // traces true
With anything you create using the new keyword, though,
you can be confident that it will have a __constructor__
property.