5

What's the difference between these 2 implementations of prototypal inheritance, and considering that we're working with 2 different "prototypes" (the prototype property that's only on functions, and the internal prototype), and how do these implementations differ in their prototype chain lookup? Also, does the first implementation (using the prototype property) rely on our use of the new operator?

Assigning to a functions's prototype property and using the new operator:

function foo() {}

foo.prototype.output = function(){
   console.log('inherits from Function.prototype property');
};

bar = new foo();
bar.output();

Storing a function in an object literal and using the Object.create() method:

var foo = {
  output: function(){
    console.log('inherits from the internal prototype');
  }
};

var bar = Object.create(foo);
bar.output();
Balázs Édes
  • 13,452
  • 6
  • 54
  • 89
shmuli
  • 5,086
  • 4
  • 32
  • 64
  • One difference is that `Object.create` is newer, and is not supported in IE8 and below – keune Apr 26 '15 at 18:48
  • I don't see prototypal **inheritance** in your examples. – hindmost Apr 26 '15 at 18:52
  • @hindmost can you elaborate please? – shmuli Apr 26 '15 at 18:57
  • I think what @hindmost means is that it's not inheritance (top-to-bottom), it's rather **delegation**. Objects rely on other objects for functionalities, not on their "ancestors" since they don't have any. – Kyll Apr 26 '15 at 18:57
  • possible duplicate of [Understanding the difference between Object.create() and new SomeFunction()](http://stackoverflow.com/questions/4166616/understanding-the-difference-between-object-create-and-new-somefunction) – Claies Apr 26 '15 at 18:58
  • @Kyll I agree that it's actually delegation, the spec refers to this as "prototypal inheritance" however. I'd rather not get stuck on semantics. – shmuli Apr 26 '15 at 18:59
  • I also use the term **inheritance**, yeah is the not inheritance in the classical sense, but JS doesn't have the classical OO model (it can be simulated though) –  Apr 26 '15 at 19:00
  • This is a well answered topic in SO. What would make it more clear? A diagram? An audio lecture? – jdphenix Apr 26 '15 at 19:13
  • @jdphenix a diagram would be great! – shmuli Apr 26 '15 at 19:18
  • 1
    Kyll fetched Kyle SImpson's diagram on the topic. I'd recommend reading the whole linked chapter myself. – jdphenix Apr 26 '15 at 19:25

3 Answers3

3

The main difference is in the way it is used, and the dangers associated.

The first one forces you to use new when you want to create a new object. The syntax is fairly ugly (SomeConstructor.prototype.method), and it bears one major flaw: calling a constructor which adds properties (this.name = nameParam...) without new will apply the construction to the global object. The behaviour of the constructor is weird (create new object which delegates to SomeConstructor.prototype, then apply the constructor to the new object, then if the constructor returns something replace the object by the something). Plus, in your example, foo itself is not usable, you have to create a new object in order to access its functionalities.

The second one, Object.create, doesn't force you to use any syntoxic quirk. You don't have the global pollution risk. The object foo has functionalities that can already be used without creating a new object, and bar will simply borrow these functionalities. This kind of pattern can also make it easier to implement factories (without news to replace everywhere) and object pools if needed.

Eric Eliott talks about it very well, and Kyle Simpson wrote a whole book about prototype delegation!

Now, here's how the lookup happens:

  • With the constructor, the lookup is done on Constructor.prototype (not the actual internal prototype of the constructor, but its prototypeproperty. If you find it confusing, congratulations, you are human). Additional properties are set in the constructor function. foo itself is not used for the lookup, foo.prototype (again, not the same as foo.__proto__ which points to Function) is the one used for it.
  • With Object.create the lookup is done on the object itself (foo). There's no prototype non-prototype property on the object.

There's very funny diagrams on this page of Kyle Simpson's book which explain the thing further.

More on new in this question: Is JavaScript's "new" keyword considered harmful?

Community
  • 1
  • 1
Kyll
  • 7,036
  • 7
  • 41
  • 64
  • @Kyll what differs as far as the "lookup" is concerned? These implementations use 2 different `prototype` objects, no? – shmuli Apr 26 '15 at 19:13
  • What I'm getting at is, how does the delegation happen in these 2 implementations? How do they differ in how they trace through the prototype lookup? Can you post a simple diagram please? – shmuli Apr 26 '15 at 19:17
  • Kyle Simpson did an amazing diagram, let me fetch it if I can. – Kyll Apr 26 '15 at 19:18
1

OK, first of all, in case you didn't know, Object.create is based upon the following pattern proposed by Douglas Crockford (read more here):

function create (proto) {
  function f () {}
  f.prototype = proto;
  return new f();
}

Now, you must be aware that the above code does not yield the same result in case you pass null as parameter.
The ECMAScript algorithm for creating new objects (read more here) states that if the prototype of a function is set to null (or any non object), when you're trying to use new f() syntax, the newly created object will inherit from Object.prototype.
If you use Object.create(null), the newly created object will have its [[Prototype]] internal member set to null, which means the prototype chain will stop there (your object doesn't get hasOwnProperty and other stuff from Object.prototype as normal object would).

var o1 = create(null);
var o2 = Object.create(null);
Object.getPrototypeOf(o1) === Object.prototype // true
Object.getPrototypeOf(o2) === Object.prototype // false
Object.getPrototypeOf(o2) === null // true
-1

The first example - foo, is a function.

The second example - foo is an object.

in the second example, bar becomes an new instance without a constructor. See - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create, to add the constructor.

As a side note, it common best practice that constructors should begin with a capital letter.

Steve Tomlin
  • 3,391
  • 3
  • 31
  • 63