Search code examples
javascriptecmascript-5prototypal-inheritanceobject-create

Object.create vs direct prototypical inheritance


I have been playing around with Object.create in the EcmaScript 5 spec, and I am trying to create a multiple inheritance type structure.

Say I have a few functions: a, b, and c. With only dealing with prototypes, I can do this:

function a () {}
a.prototype = {
    fnA = function () {},
    propA = 500
};

function b () {}
b.prototype = a.prototype;
b.prototype.fnB = function () {};
b.prototype.propB = 300;

function c () {}
c.prototype = b.prototype;
c.prototype.fnC = function () {};
c.prototype.propC = 200;

But using Object.create, I would do something this:

function a() {}
a.prototype = {
    fnA = function () {},
    propA = 500
};

var b = Object.create(new a());
b.fnB = function () {};
b.propB = 300;

var c = Object.create(b);
c.fnC = function () {};
c.propC = 200;

I think I get the same result both ways.

This seems kinda clunky, because I get on object back instead of a constructor function. It seems to me that doing the regular prototypical inheritance is less intrusive and makes more sense for modular applications that don't need any special treatment to work.

Am I missing anything? Is there any benefit of trying to make Object.create with making constructors? Or is this only useful for copying existing objects? I only want access to properties & functions attached to the prototype, and not functions and properties added afterward to the object.

Or what about this (or use a better deep-copy, but the idea remains the same)?

function A () {}
A.prototype = {
    fn: function () {
        console.log(this.propA + 30);
    },
    propA: 20
};

function B () {}
Object.keys(A.prototype).forEach(function (item) {
    B.prototype[item] = A.prototype[item];
});
B.prototype.propA = 40;

function C () {}
Object.keys(B.prototype).forEach(function (item) {
    C.prototype[item] = B.prototype[item];
});
C.prototype.fn = function () {
    console.log(this.propA + 3);
};


var a = new A(),
    b = new B(),
    c = new C();

a.fn();
b.fn();
c.fn();

Solution

  • Actually you don't get the same result both ways. Consider this line:

    b.prototype = a.prototype;
    

    What this is doing is setting b.prototype to exactly the same object reference as a.prototype. If you alter the first object (say by adding a fnB method) you will also alter the second object. They are the same thing. At the end of your first set of code you will have three prototypes that are all exactly the same, with the same methods and properties.

    The whole point of prototypal inheritance is that you define an "interesting" object (that is, with all the behavior you want) and then clone it with Object.create and modify the clones to suit your needs (usually by modifying its properties, not its methods).

    So suppose you have an adder object:

    var adder = {x: 0, y: 0};
    adder.execute = function () {
      return this.x + this.y;
    }
    

    Then you create a clone and set its properties:

    var myadder = Object.create(adder);
    myadder.x = 1;
    myadder.y = 2;
    console.log(myadder.execute()); // 3
    

    Now obviously this is a silly example, but is shows that you can think of prototypal inheritance without having to write constructors and explicit prototypes on those constructors.