Did I understand correctly how JavaScript looks for the property?
Essentially, yes. But it's important to note that the underlying prototype of an object is set by the new
operation to point to the object referenced by the constructor function's prototype
property, at which point if you pointed the constructor's prototype
property at a completely different object, it wouldn't have any effect on existing children. The children refer to the object, not the property.
So in general, property lookup works like this:
- Does the property exist on the object itself? If so, use its value.
- No, does the property exist on the object's underlying prototype? If so, use its value.
- Does it exist on its prototype's prototype? If so, use the value.
- And so on until we find the property, or run out of prototypes.
Let's throw some ASCII-Art at what you're building there, just for fun:
+-----------+ | Parent |<---------------------------+ +-----------+ | | prototype |---------->+-------------+ | +-----------+ +--->| (object) | | | +->+-------------+ | | | | constructor |--+ +------------------+ | | | say |--------->| (function) | | | +-------------+ +------------------+ | | | alert("parent"); | | | +------------------+ | | | +--------------------------------------------------+ | | +-----------------------+ | +-----------+ | | | Child |<---------------------------+ | | +-----------+ +-------------+ | | | | prototype |------+--->| (object) | | | | +-----------+ | +-------------+ | | | | | constructor |--+ | | | | __proto__ |----+ +------------------+ | | | say |--------->| (function) | | | +-------------+ +------------------+ | | | alert("child"); | | +-----------+ | +------------------+ | | c | | | +-----------+ | | | __proto__ |------+ | +-----------+ | | +-----------+ | | p | | +-----------+ | | __proto__ |-----------------------------------------------------------+ +-----------+
...where __proto__
is the hidden property that represents the object's link to its prototype. (Some engines actually expose this, and there's a proposal to add that to the standard.)
As you can see, Child.prototype
and the c
instance's __proto__
both point to the same object (and similarly for Parent.prototype
and p
's __proto__
).
The reason I make the distinction in my first paragraph above is this:
function Foo() {
}
Foo.prototype.a = 1;
var f1 = new Foo();
Foo.prototype = {b: 2}; // Putting an entirely new object on `prototype`, not just modifying it
var f2 = new Foo();
console.log(f1.a); // "1"
console.log(f1.b); // "undefined"
console.log(f2.a); // "undefined"
console.log(f2.b); // "2"
f1
and f2
end up having completely different prototypes, and by the end of the above, f1
's __proto__
no longer refers to the same object Foo.prototype
refers to.
For this reason, except in very specific situations (like your extend
function), I strongly recommend not assigning new objects to the prototype
property of a constructor function, because it can get really confusing. :-) Your extend
function is, again, an exception.