
Originally Posted by
???
THANK YOU SO MUCH!!!!
So no problems or major questions so far? Good. If you do have any, or if you think I'm rushing over topics too quickly, do tell me so! Now is especially the time as I'm going to write about more technical issues. And of course, make sure you play around with the things I mention - I don't know about you, but I learn best by doing.
I showed previously that by using the prototype object, newly-created objects can be given members. In fact, so long as the prototype object can be referenced, new members can be given to all "instances" even after creation!
Code:
function MyObject() {}
MyObject.prototype.method = function () {
return 'value';
};
var myObject = new MyObject();
myObject.method(); // evaluates to 'value'
MyObject.prototype.anotherMethod = function () {
return 'another value';
};
myObject.anotherMethod(); // evaluates to 'another value';
If you understand the concept of pointers or references, this shouldn't come as much of a surprise, though you might not have thought about it: objects, including the prototype object, are referenced, therefore any changes can be observed by anything that references that object.
To break this chain, one can create a new object to act as a prototype, though we could still make changes if the old prototype object is referenced somewhere.
Code:
function MyObject() {}
MyObject.prototype.method = function () {
return 'value';
};
var a = new MyObject();
typeof a.method; // evaluates to 'function'
MyObject.prototype = new Object();
MyObject.prototype.anotherMethod = function () {
return 'another value';
};
var b = new MyObject();
typeof a.method; // evaluates to 'function'
typeof b.method; // evaluates to 'undefined'
typeof a.anotherMethod; // evaluates to 'undefined'
typeof b.anotherMethod; // evaluates to 'function'
So, how does this isolation work? Every native object (and some host objects) has a hidden property named [[Prototype]]. When the object is created, the prototype object from the constructor function is assigned to that property. In this way, every object keeps track of its own prototype.
Keep in mind that I wrote "every...object". This means that even the prototype for an object has a [[Prototype]] property. This, as you might guess, forms the prototype chain, an important concept surrounding objects. The chain always ends with the Object prototype object.
Every time you use a member access operator (the dot [.] and square bracket operators), you use the prototype chain. Consider myObject.member. First, myObject is searched for a property named member. If no match is found, the search starts again with the object referenced by the [[Prototype]] property. This continues until [[Prototype]] is null, in which case the value, undefined is returned.
The way in which the prototype chain search works allows one to override the value of a property on a given object: by assigning a value to a property, you actually create that property on the object.
Code:
function MyObject() {}
MyObject.prototype.member = 0;
var a = new MyObject();
// At this point, the object, a, has no direct properties. They are all obtained from the
// prototype chain.
a.member; // evaluates to 0.
MyObject.prototype.member = 5;
a.member; // evaluates to 5.
a.member = 9;
// The object, a, now has its own property, member. A search for that property will now end
// immediately.
a.member; // evaluates to 9.
You might have seen by now the scope for inheritance: by assigning an object to the prototype property of a constructor function, the objects created thereafter will "inherit" the members of that object. However, limitations quickly become obvious:
Code:
function Vehicle(maxSpeed) {
this.maximumSpeed = maxSpeed;
}
Vehicle.prototype.shortestJourneyTime = function (distance) {
return distance / this.maximumSpeed;
};
function Car(make, model) {
/* ... */
}
Car.prototype = new Vehicle( /* No sensible value! */ );
Whilst in the example above, Car "instances" will have a maximumSpeed property and a shortestJourneyTime method, creating the prototype object is rather nonsensical. Whilst the Car constructor function could (and should) be modified to include a speed argument, it does illustrate the fact that base objects cannot be configured in this way unlike in C++, for instance. Things are worse, of course, if the Vehicle constructor were to calculate a value.
Of course, in a language as flexible as this, there are alternate forms of inheritance. When the constructor of the base object is of little or no importance, one can just copy methods on to the prototype.
Code:
Function.prototype.copyMethods = function (constructor) {
for (var i = 1, n = arguments.length; i < n; ++i) {
var propertyName = arguments[i];
this.prototype[propertyName] = constructor.prototype[propertyName];
}
};
Car.copyMethods(Vehicle, 'shortestJourneyTime');
Adding to that, one can call the base object constructor in a similar way to constructors in Java and C++:
Code:
function Car(make, model, maxSpeed) {
Vehicle.call(this, maxSpeed);
/* ... */
}
Though this won't work on earlier IE versions without supporting code to emulate the call method.
There's yet another approach, though I'd like to draw your attention to something else, first. You might have noticed in the examples so far that the constructor function doesn't return anything. As you might have guessed, this is normal. However, constructor functions can return values, though you'll rarely ever need to do so. There isn't much to say about this subject, except this: constructor functions can only return object references. Any other type, including null, is ignored and acts like returning this - the newly constructed object. However, if you do return an object reference, that reference is the result of evaluating the new expression:
Code:
function MyObject(object) {
return object;
}
new MyObject('value'); // evaluates to a MyObject "instance".
new MyObject(null); // evaluates to another MyObject "instance".
new MyObject(document); // evaluates to a reference to the document host object.
We can use this with our constructors: create a new Vehicle, modify it, and return it from Car!
Code:
function Car(make, model, maxSpeed) {
var object = new Vehicle(maxSpeed);
/* Add other members to object... */
return object;
}
Unfortunately, this approach prevents us using the prototype object of Car.
A lot to take in, I know. ECMAScript is more complicated than many people realise.
I realise that began to ramble, but it's late here, and I'd like to get to sleep. Still, I hope you can make sense of what I've written.
Bookmarks