Tuesday, October 13, 2009

an issue caused by prototype inheritance in javascript

This issue is not obvious, in some case, it does not even cause any error at all. But fixing this issue brings some benefit. Here the use case.

var user_constructor = function() { alert(this.constructor); //function() { alert(this .. }; this.constructor.count++; this.Id = this.constructor.count; }; user_constructor.count = 0; var c = new user_constructor(); alert(c.Id); //1 alert(c.constructor == user_constructor); //true alert(user_constructor.count); //1

We know that constructor is also an object, (an object is not necessarily a constructor), in our case our constructor has property "count" to count the instance created by the constructor. And the Id is this last count. Very straight forward. Now if we want add in inheritance, we can implement as follow.

var user_constructor = function() { alert(this.constructor); //function Object() { [native code] } this.constructor.count++; this.Id = this.constructor.count; }; user_constructor.count = 0; var user_prototype = { Name: "Unknown" }; user_constructor.prototype = user_prototype; var c = new user_constructor(); alert(c.Id); //NaN alert(c.constructor == user_constructor); //false alert(user_constructor.count); //0

Suddenly, the code broke down. Is it because the fault of the prototype object? No. We need to have deeper understand how constructor works. When an object is created by using "new user_constructor()", the javascript runtime need to determine what constructor is used used to create an object "this". But how. According to ECMAScript Language Specification Edition 3

ECMAScript supports prototype-based inheritance. Every constructor has an associated prototype,and every object created by that constructor has an implicit reference to the prototype(calledthe object’s prototype)associated with its constructor.

To implement this feature, the runtime need to create "this" that has be behavior of user_constructor.prototype, so runtime will use user_constructor.prototype.constructor function to create "this". In the first case, there is no prototype based inheritance. So user_constructor.prototype is [object Object], naturally, user_constructor.prototype.constructor should [object Object] as well, but it is actually function() { alert(this .. }. Why. I don't know exactly, but this may be because user_constructor has no user assigned prototype, so the object should be created by user_constructor itself. In the second case, user_constructor.prototype is user_prototype, which is create by "function Object() { [native code] }". So runtime will use that function as constructor. After we rationalize this, we can easily fix this behavior as follow by adding one line "user_prototype.constructor = user_constructor;"

var user_constructor = function() { alert(this.constructor); //function() { alert(this .. }; this.constructor.count++; this.Id = this.constructor.count; }; user_constructor.count = 0; var user_prototype = { Name: "Unknown" }; user_constructor.prototype = user_prototype; user_prototype.constructor = user_constructor; var c = new user_constructor(); alert(c.Id); //NaN alert(c.constructor == user_constructor); //false alert(user_constructor.count); //0

No comments:

Post a Comment