How PROTOTYPE works behind the scenes?


D

Dolaameng

I am reading the book <Professional javascript for web developers> by
Nicholas C. Zakas. On page 162 he gives an example of how overwriting
the prototype with an object literal can cause the constructor testing
to fail. Here is the code in the book,
///////////////////////////////////////////////////
function Person(){
}

Person.prototype = {
name : "Nicholas",
age : 29,
job : "Software Engineer",
sayName : function () {
alert(this.name);
}
};

var person = new Person();
alert(person.constructor == Person); //false
alert(person instanceof Person); //true
///////////////////////////////////////////////////
As the author explained, the prototype of the constructor Person has
been overwritten by an object literal. As a result, person.constructor
will not evaluate to Person anymore ( it is Object now) and somehow
the instanceof operator still works. Everything seems straightforward.

But something interesting happened when I tried the code myself and
changed the execution order of the two code snippets by accident-- I
put the line of creating a new instance "var person = new Person();"
BEFORE the overwriting of prototype, so it is now,
///////////////////////////////////////////////////
function Person(){
}

var person = new Person(); //now before the prototype overwriting
Person.prototype = {
name : "Nicholas",
age : 29,
job : "Software Engineer",
sayName : function () {
alert(this.name);
}
};

alert(person.constructor == Person); //true
alert(person instanceof Person); //false
///////////////////////////////////////////////////
As a consequence, the testing for person.constructor evaluates to true
while the instanceof operator evaluates to false now.

So I guess this is what is going on behind the scenes--everytime when
a new instance is created by the constructor, the instance's hidden
__proto__ property will be dynamically set to the current prototype
property of the constructor. That is why in the second code the
instance person's constructor still points to Person.

And the 'instanceof' works somehow like this: it compares the
instance's hidden __proto__ against the prototype property of the
constructor. If the constructor's prototype property is on the
instance's __proto__ chain, it will evaluates to true. So as in the
first code, even when the prototype's constructor property is not
correctly set, the instanceof operator will still work.

I am still not quite sure about these prototype and instanceof
concepts. So any supplementary comments will be welcomed.
 
Ad

Advertisements

D

Dmitry A. Soshnikov

First, <URL:
http://groups.google.ru/group/comp....c052b/a620e348cfce5a6b?hl=en#9d13a47b3912f703>
- just a day ago there was similar topic.
So I guess this is what is going on behind the scenes--everytime when
a new instance is created by  the constructor, the instance's hidden
__proto__ property will be dynamically set to the current prototype
property of the constructor. That is why in the second code the
instance person's constructor still points to Person.

You know it all yourself ;)
And the 'instanceof' works somehow like this: it compares the
instance's hidden __proto__ against the prototype property of the
constructor. If the constructor's prototype property is on the
instance's __proto__ chain, it will evaluates to true. So as in the
first code, even when the prototype's constructor property is not
correctly set, the instanceof operator will still work.

Also true, but more correctly, not only `prototype' property of an
constructor function, but `instanceof' analyzes all the prototype
chain.
I am still not quite sure about these prototype and instanceof
concepts. So any supplementary comments will be welcomed.

If it will help, here's pseudo code of what is constructor (function)
is and how object is created by the function:

Function object:

F = new NativeObject();

F.[[Class]] = "Function"

..... // other properties

// that code executes with call expression
F.[[Call]] = callObject

// general internal constructor of all object
F.[[Construct]] = internalConstructor

..... // other properties

// a prototype of objects
// which will be created
// via this function
__objectPrototype = {};
__objectPrototype.constructor = F // {DontEnum}
F.prototype = __objectPrototype

So, that's how any native function looks like.

The main part here is [[Construct]] internal property (method) of the
function - exactly this internal method *creates* and allocate memory
for the object.

After the object is created, then [[Call]] internal method is called
to *initialize* newly created object.

Algorithm of the [[Construct]] internal method (pseudo code):

F.[[Construct]](initialParameters):

O = new NativeObject();

// intenal [[Class]] property is set to "Object"
O.[[Class]] = "Object"

// get the object on which
// references *at the current moment* F.prototype
var __objectPrototype = F.prototype;

// if __objectPrototype is an object, then:
O.[[Prototype]] = __objectPrototype
// else:
O.[[Prototype]] = Object.prototype;
// where O.[[Prototype]] is a original
// implicit reference to the prototype of
// newly created object

// initialization of the newly created object
// calling F.[[Call]]; passes:
// as `this' - newly created object - O,
// parameters are the same as initialParameters of F
R = F.[[Call]](initialParameters); this === O;
// where R is result of the work of [[Call]]

// if R is an object
return R
// else
return O

The main thing in this algorithm that you'll have newly created object
in code of the constructor as `this' value. After that, if you'll
return something from the constructor function - exactly this object
will be a result of `new ...' expression, otherwise (in normal case) -
newly created object.

The second main thing - that original prototype of an object - is a
[[Prototype]] internal property which as you can see is taken from the
`prototype' property of an constructor function. After [[Prototype]]
has been set, it cannot be changed anymore on new object (exclude non-
standard properties such as __proto__).

/ds
 
T

Thomas 'PointedEars' Lahn

Dolaameng said:
[...]

Person.prototype = {
name : "Nicholas",
age : 29,
job : "Software Engineer",
sayName : function () {
alert(this.name);
}
};

As [Nicholas Zakas] explained, the prototype of the constructor Person
has been overwritten by an object literal. [...]

Only very roughly speaking. What really happened is that the Object
_initializer_ expression right-hand side caused a new Object instance (that
is, an object that has the original Object.prototype object next in its
prototype chain) to be created and initialized (as it is equivalent to `new
Object' followed by property initializations).

It then evaluates to (the algorithm returns) a reference to that instance,
which is assigned to the `prototype' property (which previous value, a
reference to another object, is overwritten as a result).

See the ECMAScript Language Specification, Editions 3 [ES3] and 5 [ES5],
(section) 11.1.5.
But something interesting happened when I tried the code myself and
changed the execution order of the two code snippets by accident-- I
put the line of creating a new instance "var person = new Person();"
BEFORE the overwriting of prototype, so it is now,
///////////////////////////////////////////////////
function Person(){
}

var person = new Person(); //now before the prototype overwriting
Person.prototype = {
name : "Nicholas",
age : 29,
job : "Software Engineer",
sayName : function () {
alert(this.name);
}
};

alert(person.constructor == Person); //true
alert(person instanceof Person); //false
///////////////////////////////////////////////////
As a consequence, the testing for person.constructor evaluates to true
while the instanceof operator evaluates to false now.

So I guess this is what is going on behind the scenes--everytime when
a new instance is created by the constructor, the instance's hidden
__proto__ property will be dynamically set to the current prototype
property of the constructor.

No. The internal [[Prototype]] property of the instance will be set to the
current prototype property of the constructor, and that is what the
proprietary __proto__ property reflects if supported, and what ES5's
Object.getPrototypeOf() method would return if passed a reference to the
instance.

See ES3/5, 11.2.2, and ES5, 15.2.3.2.
That is why in the second code the instance person's constructor still
points to Person.

Yes. The prototype object is a different one then. As a result, this is
not a good way to fix anything as objects created with the same constructor
would have a different prototype chain.
And the 'instanceof' works somehow like this: it compares the
instance's hidden __proto__ against the prototype property of the
constructor. If the constructor's prototype property is on the
instance's __proto__ chain, it will evaluates to true. So as in the
first code, even when the prototype's constructor property is not
correctly set, the instanceof operator will still work.

Iff

s/hidden __proto__/internal [[Prototype]] property/g
s/constructor's/object's/

then yes. See ES3/5, 11.8.6.
I am still not quite sure about these prototype and instanceof
concepts. So any supplementary comments will be welcomed.

Read ES3/5, if dozens of previous discussions about this subject would not
suffice.

One OP of yours on this subject would have sufficed.

<http://jibbering.com/faq/#posting>


PointedEars
--


realism: HTML 4.01 Strict
evangelism: XHTML 1.0 Strict
madness: XHTML 1.1 as application/xhtml+xml
-- Bjoern Hoehrmann
 
Ad

Advertisements

D

Dolaameng

By odd coincidence I asked a question about the same chapter in Zakas
yesterday. You might want to check out Dimitry's reply to me, also.

JFTR the reason Zakas made the statement that puzzled me was exactly the
case that this poster shows here. i.e. when you replace the prototype of
a constructor function with your own object and neglect to include the
constructor property.

Until I read further it sounded like Zakas was making a general
statement that the constructor property was less reliable than
instanceof. It became clear what he meant later.

Thanks guys. These are really useful details and something worth
digesting diligently.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top