1

I'm trying to understand inheritance in Javascript.

I know that every object has a prototype, which is an object it inherits properties from. I know that the .prototype property exists only on functions, and it is the object that will be set asthe prototype of objects created from this function, when it's used as a constructor.

I know that clasically an object's prototype is inaccessible, although some browsers support the __proto__ property. (But since it's not a 'classical' part of the language, I want to understand how to use the language without it).

So if all of this is correct (?), I want to underatand what the standard way is to define inheritance chains.

The only approach I can think of is this:

All objects that I want them to inherit from another object, have to be created through a constructor function. Their 'base object' would be set as the .prototype of their constructor.

And when I want one of them to be a 'base object' of other objects, I'd set it as the .prototype of another constructor. And so on.

This seems odd. Is there a way (in 'normal' JS) to set an object's 'base' directly? Or do I have to use constructors in the way described above, in order to create inheritance chains?

What would be the 'standard' way to create inheritance? Is the approach I describsd the standard approach?

Aviv Cohn
  • 15,543
  • 25
  • 68
  • 131
  • You can use [`Object.create()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) – PM 77-1 Jan 08 '15 at 05:52
  • @PM77-1 `Object.create` can inherit but can it create a chain? That is, can it Object.create an object.created object? Because to the best of my knowledge, objects themselves don't have prototypes. Only constructors (functions) do. – slebetman Jan 08 '15 at 06:16
  • @slebetman Prototypes are objects. Read the following answer to understand how prototypal inheritance in JavaScript works: http://stackoverflow.com/a/8096017/783743 – Aadit M Shah Jan 08 '15 at 06:37
  • @AaditMShah: Prototypes are objects but only functions have prototypes (a property called "prototype" that behaves like prototypes). Trying to add a prototype property to regular objects will only add a property whose name is "prototype". They don't get inherited in the same way. – slebetman Jan 08 '15 at 06:40
  • 1
    @slebetman You don't get the point. You can create instances of a prototype without the need of a constructor function by using `Object.create`. For example: `var a = {}; var b = Object.create(a); var c = Object.create(b); var d = Object.create(c);`. Here I created a prototype chain using `Object.create` in which `d -> c -> b -> a -> Object.prototype -> null` (i.e. `d` inherits from `c` which inherits from `b` which inherits from `a` which inherits from `Object.prototype` which inherits from `null`). Functions have a `prototype` property, but all objects have a special `[[prototype]]` property – Aadit M Shah Jan 08 '15 at 06:45
  • Yes I know. But since `b` is an object not a constructor, `b` cannot then add to it's prototype. You've basically created a useless chain. I see wmock's answer for how you'd do it by extending `b` directly but that's like parasitic inheritance (cloning) not actual extension of the prototype chain. – slebetman Jan 08 '15 at 06:48
  • @slebetman How is it a useless chain? What do you mean by "`b` cannot then add to it's prototype"? You make absolutely no sense. Using `Object.create` is not like parasitic inheritance (cloning) at all. It is indeed real prototypal inheritance. When I write `var a = {}; var b = Object.create(a);` then `b` is actually inheriting from `a`. Actually. Inheriting. From. `a`. – Aadit M Shah Jan 08 '15 at 07:21
  • It's a useless chain because there's no use for doing it. The reason you create inheritance chain is to compose objects. b inherit from a then adds a few more methods/properties, c inherits from b then add a few more methods/properties. Otherwise c might as well be constructed form a because there's no functional difference. – slebetman Jan 08 '15 at 07:57
  • @slebetman Nothing is stopping you from writing `var a = {}; var b = Object.create(a); b.foo = 10; b.bar = 20;`. Also, you didn't answer my questions. What do you mean by "since `b` is an object not a constructor, `b` cannot then add it's prototype"? Also, how is using `Object.create` like parasitic inheritance (cloning) and not actual inheritance? – Aadit M Shah Jan 08 '15 at 20:31

3 Answers3

1

The main manner which JavaScript supports inheritance is through prototypal inheritance. Specifically, objects in JavaScript delegate to other objects whenever a property lookup can't be found on the initial object. This delegation continues until the JavaScript engine reaches Object.prototype in which the property is either found or an error is thrown.

The current best practice to create objects with specific objects as their prototype is to use Object.create - you can take a look at more information here.

Here's an example:

var methods = {
  method1: function () { console.log( 'something' ); },
  method2: function () { return 'cool'; }
};

/*
 * Now firstObj will delegate to methods whenever a property lookup can't
 * be found on firstObj itself
*/
var firstObj = Object.create( methods ); 

// You can add custom properties on firstObj
firstObj.someOtherProperty = 'custom property'; 

/*
 * You can create a chain of delegations! Property lookup first happens on secondObj.
 * If its not found there, it looks up the property in firstObj. If its not found there,
 * then it looks up the property in methods. Finally, if not found, it tries
 * Object.prototype
*/
var secondObj = Object.create ( firstObj );
wmock
  • 5,382
  • 4
  • 40
  • 62
  • So let me see if I understand: when I want to create a fresh new object, and I want it to inherit from another object, I would simply use `Object.create`. And when I have a constructor function, that I want all of it's created objects to inherit some object, I would set the `.prototype` of the constructor to that object. Are those the standard, common way to implement inheritance? – Aviv Cohn Jan 08 '15 at 06:17
  • Yes, these are the most standard ways but as others have mentioned, using the `constructor` pattern is very misleading in JavaScript because JavaScript was not designed with `classes` in mind (instead, JavaScript is a prototype based language). I would say that `Object.create` is the preferred way to natively handle `inheritance` in JavaScript because it is explicit about the relationship between the "parent" object and the object itself. – wmock Jan 08 '15 at 14:17
1

Inheritance in JavaScript is a little difficult to understand at first because:

  1. JavaScript is a prototypal object-oriented programming language (i.e. objects directly inherit from other objects). This means that there's no distinction between classes and objects. Objects which are used as classes are called prototypes.
  2. Unfortunately, the traditional way to create an instance of a prototype is by using new (which makes people think that the instance inherits from the constructor function, and not the prototype). This is called the constructor pattern, and it's the main reason of confusion in JavaScript.

For this reason Object.create was introduced. It allowed objects to directly inherit from other objects. However, Object.create is slow as compared to using new. I had the same problem that you did and I was looking for an alternative; and I did come up with one.

The Traditional Way of OOP in JavaScript

Consider the following code:

function Person(firstname, lastname, gender) {
    this.firstname = firstname;
    this.lastname  = lastname;
    this.gender    = gender;
}

Person.prototype.getFullname = function () {
    return this.firstname + " " + this.lastname;
};

Man.prototype             = new Person;
Man.prototype.constructor = Man;

function Man(firstname, lastname) {
    Person.call(this, firstname, lastname, "M");
}

var bobMarley = new Man("Bob", "Marley");

alert(bobMarley.getFullname());

This way of writing code suffers from several problems:

  1. There is no encapsulation. The constructor function and the prototype methods are defined all over the place. It look incoherent. Like shaghetti. It doesn't look like one logical unit.
  2. We make Man.prototype inherit from Person.prototype by setting it to new Person. However, in doing so we're initializing the firstname, lastname and gender properties on Man.prototype which is wrong.

The New Way of OOP in JavaScript

With the introduction of Object.create we can now write code like this:

function Person(firstname, lastname, gender) {
    this.firstname = firstname;
    this.lastname  = lastname;
    this.gender    = gender;
}

Person.prototype.getFullname = function () {
    return this.firstname + " " + this.lastname;
};

Man.prototype             = Object.create(Person.prototype);
Man.prototype.constructor = Man;

function Man(firstname, lastname) {
    Person.call(this, firstname, lastname, "M");
}

var bobMarley = new Man("Bob", "Marley");

alert(bobMarley.getFullname());

The only change is that instead of Man.prototype = new Person we write Man.prototype = Object.create(Person.prototype). This solves the second problem of the traditional method. However, the code still looks like spaghetti.

However, Object.create is quite powerful. You could also use it to write object-oriented code without creating constructor functions at all. Some people call this the initializer pattern:

var person = {
    init: function (firstname, lastname, gender) {
        this.firstname = firstname;
        this.lastname  = lastname;
        this.gender    = gender;
    },
    getFullname: function () {
        return this.firstname + " " + this.lastname;
    }
};

var man = Object.create(person, {
    init: {
        value: function (firstname, lastname) {
            person.init.call(this, firstname, lastname, "M");
        }
    }
});

var bobMarley = Object.create(man);
bobMarley.init("Bob", "Marley");
alert(bobMarley.getFullname());

This solves all the problems of the traditional method. However, it also introduces some new problems of its own:

  1. The way of creating instances of prototypes is not consistent with the way of creating object literals.
  2. You have to create an instance using Object.create and then initialize the new object using init. This is much slower than simply using new.

My Way of OOP is JavaScript

To solve this problem, I wrote my own functions for OOP in JavaScript:

var Person = defclass({
    constructor: function (firstname, lastname, gender) {
        this.firstname = firstname;
        this.lastname  = lastname;
        this.gender    = gender;
    },
    getFullname: function () {
        return this.firstname + " " + this.lastname;
    }
});

var Man = extend(Person, {
    constructor: function (firstname, lastname) {
        Person.call(this, firstname, lastname, "M");
    }
});

var bobMarley = new Man("Bob", "Marley");

alert(bobMarley.getFullname());

function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

function extend(constructor, properties) {
    var prototype = Object.create(constructor.prototype);
    var keys      = Object.keys(properties);
    var length    = keys.length;
    var index     = 0;

    while (index < length) {
        var key = keys[index++];
        prototype[key] = properties[key];
    }

    return defclass(prototype);
}

I defined two functions defclass and extend for OOP in JavaScript. The defclass function creates a “class” from a prototype. This is possible because prototypes and classes are isomorphic.

The extend function is for inheritance. It creates an instance of the prototype of a constructor and copies some properties onto it before returning the “class” of the new prototype.

This is the way I currently create prototype chains in JavaScript. It has the following advantages over other methods:

  1. Every “class” is encapsulated. There are no prototype methods dangling all over the place. It doesn't look like spaghetti.
  2. The extend function uses Object.create for inheritance. Hence no extra properties are added to the new prototype. It's a blank prototype.
  3. You don't have to worry about resetting the constructor property on the prototype. It is automatically done for you.
  4. The defclass and the extend functions are consistent unlike object literals and the Object.create functions in the initializer pattern.
  5. We create instances using new instead of Object.create and init. Hence the resulting code is much faster.

I could be wrong now, but I don't think so. Hope that helps.

Community
  • 1
  • 1
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • I agree with the examples you provided but I'm curious what you mean by `Object.create` is slow? Could you elaborate more on what is slow about it? – wmock Jan 08 '15 at 14:13
  • See for yourself: http://jsperf.com/new-vs-object-create. Using `Object.create` is orders of magnitude slower than using `new`. – Aadit M Shah Jan 08 '15 at 14:47
  • Thanks for providing that! Useful info to know! – wmock Jan 08 '15 at 15:18
-1

You can inherit by 2 ways in javascript - classical and prototypal

Classical

function inherit (C, P) {
    var F = function () {};
    F.prototype = P.prototype;
    C.prototype = new F();
}

Prototypal

function inherit (o) { 
    function F() {}
    F.prototype = o;
    return new F(); 
}
Alok Ranjan
  • 100
  • 4
  • No. Both of them are prototypal. – Aadit M Shah Jan 08 '15 at 06:46
  • The goal of implementing classical inheritance is to have objects created by one constructor function Child() get properties that come from another constructor Parent(). In prototypal pattern there are no classes involved; here objects inherit from other objects. You can think about it this way: you have an object that you would like to reuse and you want to create a second object that gets its functionality from the first one. – Alok Ranjan Jan 08 '15 at 06:53
  • Sure, but you haven't implemented classical inheritance. For example, when you write `function Parent() {}; function Child() {}; inherit(Child, Parent);` then you are still using prototypal inheritance. Mind you, `Child` is not inheriting from `Parent`. However, `Child.prototype` is inheriting from `Parent.prototype`. That's prototypal inheritance. Not classical inheritance. A constructor function is not a "class" in the traditional sense. It is a part of a "class". Read the following answer for more details: http://stackoverflow.com/a/27822158/783743. What you've created is a bad abstraction – Aadit M Shah Jan 08 '15 at 07:07