11

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!

ambertch
  • 7,581
  • 4
  • 28
  • 40

2 Answers2

10

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.

vhallac
  • 13,301
  • 3
  • 25
  • 36
  • Thanks for the mckoss article link - just one clarification before we wrap up, I think I have this correct: my suggested alternative (Derived.prototype = Base.prototype) is fine if nobody screws with the prototype, the reason why we want to do "Derived.prototype = new Base" (which will set the prototype of Derived to a new object that has its [[prototype]] set to Base.prototype), is to make damned sure that any modifications to Derived's prototype CANNOT affect Base's prototype. – ambertch Mar 23 '11 at 21:17
  • Your understanding and mine are the same. Since I am not very well-versed in Javascript, there may be some other subtleties I've missed, though. Also, if you need to instantiate the derived class, `Derived.prototype.constructor` will be `Base.prototype.constructor`, so it will call the wrong function if you use `new Derived`. I think. – vhallac Mar 23 '11 at 22:11
2

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.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Thanks Felix, your's + Dysaster's answers combined really clarified things, wish I could mark both of them – ambertch Mar 23 '11 at 21:19
  • 1
    Actually, __proto__ is in the ECMAScript 6 standard, it's also very well supported across modern browsers (IE9+) – csuwldcat Jan 04 '13 at 13:59