9

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();
hippietrail
  • 15,848
  • 18
  • 99
  • 158
beatgammit
  • 19,817
  • 19
  • 86
  • 129

2 Answers2

4

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.

psxls
  • 6,807
  • 6
  • 30
  • 50
jmbucknall
  • 2,061
  • 13
  • 14
  • Right, I guess so. But what if I copied all of the properties in a to b's prototype, then added stuff to the prototype, then did the same for c? Would this achieve the same thing? – beatgammit Jun 14 '11 at 02:43
  • 2
    Better to use `Object.create`. So, `b.prototype = Object.create(a.prototype);` And then we're using the better solution make the lesser solution better. – jmbucknall Jun 14 '11 at 02:51
  • Could I make another constructor? Say I do Object.create, can I do `function new () {} new.prototype = Object.create(b);` or something similar? I just want an exportable function... – beatgammit Jun 14 '11 at 02:53
  • Sure. You can do anything you want. There are no real steadfast rules. Just choose what feels more natural to you and go for it. – jmbucknall Jun 14 '11 at 04:02
  • As an exportable function: how about a power construction, like `function newB() { return Object.create(b); }`? – Domenic Jun 14 '11 at 04:03
  • Your answer really helped. I ended up going with John Resig's [simple inheritance](http://ejohn.org/blog/simple-javascript-inheritance/) because it seemed simple and seems to work well. – beatgammit Jun 21 '11 at 07:14
  • 4
    `execute()` throws a **ReferenceError** unless you change it to `return this.x + this.y;` – rxgx Jul 02 '11 at 07:38
0

If you need to support browsers that don't support Object.create() you can use this

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

The object function untangles JavaScript's constructor pattern, achieving true prototypal inheritance. It takes an old object as a parameter and returns an empty new object that inherits from the old one. If we attempt to obtain a member from the new object, and it lacks that key, then the old object will supply the member. Objects inherit from objects. What could be more object oriented than that?

More can be read here : Prototypal Inheritance in JavaScript

Ivan V.
  • 7,593
  • 2
  • 36
  • 53
  • Because Object.create takes a second parameter that you cannot poly fill you may want to throw an error if someone provides it. Just so you or someone else using your code would have a clear error instead of unexpected behavior: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Cross-browser_compatibility – HMR Nov 17 '13 at 02:06