Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion about setting something.prototype.__proto__

In the code for the Express module for Node.js I came across this line, setting inheritance for the server:

Server.prototype.__proto__ = connect.HTTPServer.prototype;

I'm not sure what this does - the MDC docs (https://developer.mozilla.org/en/JavaScript/Guide/Inheritance_Revisited#prototype_and_proto) seem to say that I could just do:

Server.prototype = connect.HTTPServer.prototype;

Indeed, I did this test:

var parent = function(){}
parent.prototype = {
    test: function(){console.log('test')};
}

var child1 = function(){};
child1.prototype = parent.prototype;
var instance1 = new child1();
instance1.test();     // 'test'

var child2 = function(){};
child2.prototype.__proto__ = parent.prototype;
var instance2 = new child2();
instance2.test();     // 'test'

Looks to be the same? So yah, I'm wondering what setting object.prototype.__proto is for. Thanks!

like image 390
ambertch Avatar asked Mar 22 '11 22:03

ambertch


Video Answer


2 Answers

Have a look at the diagram on this page (mckoss.com) that shows the prototype, constructor, __proto__ relations for a small hierarchy. Also the code below the diagram describes the relation quite well.

When you have a function Base, and set the prototype of the function object defined, the statement Derived.prototype = new Base; sets the __proto__ (actually the internal [[prototype]]) of Derived.prototype to Base.prototype automatically, making Derived itself a class that you can instantiate objects from. This seems the be a more standards compliant way of defining a derived class.

From what I read, __proto__ is a non-standard way of accessing the internal [[prototype]] of an object. It seems to be well supported, but I am not sure if it should be trusted.

In any case, your example Server.prototype.__proto__ = connect.HTTPServer.prototype; seems to do the derivation the other way around: first define an object, Server by defining the constructor and the proto, and then hook up the internal [[prototype]] manually to morph it into a class derived from HTTPServer.

As for your suggested alternative, Server.prototype = connect.HTTPServer.prototype;: that is a bad idea. Here, you are setting the prototype of Server to be the same object as the prototype of HTTPServer. So any changes you make to Server class will be directly reflected in HTTPServer, and will be accessible from other derived classes of HTTPServer. You can imageine the chaos if two classes derived from HTTPServer try to define the same member.

like image 156
vhallac Avatar answered Oct 20 '22 00:10

vhallac


The non-standard property __proto__ lets you set the prototype of an existing object.

In your example, both version will achieve the same effect, but there is a difference:

child1's prototype is the same as parent's prototype, whereas child2's prototype is an empty object and this empty object's prototype is the same as parent's prototype.

Of course as child2 and its prototype don't have a method test, this method will be looked up further up in the prototype chain.

Also consider this:

You want to create only one object that should inherit from another object. Now, you could write a constructor function, but JavaScript has object literal notation to create objects directly and you want to use it.

If you have a constructor function, letting the new objects inherit from another object is as easy a setting the prototype of the constructor function to that object.

Obviously this does not work for object literals. But in Firefox you can use __proto__ to set it:

var server = {
    __proto__: connect.HTTPServer.prototype,
    other: properties
};

As this property is not standard, you should avoid using it.

like image 32
Felix Kling Avatar answered Oct 19 '22 23:10

Felix Kling