Benefits of using `Object.create` for inheritance

前端 未结 4 1179
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-21 05:43

I\'ve been trying to wrap my head around the new Object.create method which was introduced in ECMAScript 5.

Usually when I want to use inheritance I do

相关标签:
4条回答
  • 2020-11-21 06:14

    I'm trying to illustrate the difference a little bit:

    Here is what basically happens when you write new Animal():

        //creating a new object
        var res = {};
    
        //setting the internal [[prototype]] property to the prototype of Animal
        if (typeof Animal.prototype === "object" && Animal.prototype !== null) {
            res.__proto__ = Animal.prototype;
        }
    
        //calling Animal with the new created object as this
        var ret = Animal.apply(res, arguments);
    
        //returning the result of the Animal call if it is an object
        if (typeof ret === "object" && ret !== null) {
            return ret;
        }
    
        //otherise return the new created object
        return res;
    

    And here is what basically happens with Object.create:

        //creating a new object
        var res = {};
    
        //setting the internal [[prototype]] property to the prototype of Animal
        if (typeof Animal.prototype !== "object") {
            throw "....";
        }
        res.__proto__ = Animal.prototype;
    
        //return the new created object
        return res;
    

    So it does the same but it doesn't call the Animal function and it also always returns the new created object. In your case you end up with two different objects. With the first method you get:

    Dog.prototype = {
        name: undefined,
        __proto__: Animal.prototype
    };
    

    and with the second method you get:

    Dog.prototype = {
        __proto__: Animal.prototype
    };
    

    You don't really need to have the name property in your prototype, because you already assigning it to your Dog instance with Animal.call(this, 'Dog');.

    Your primary goal is to let your Dog instance access all the properties of the Animal prototype, which is achieved by both methods. The first method however does some extra stuff that is not really needed in your case or can even cause unwanted results as Pumbaa80 mentioned.

    0 讨论(0)
  • 2020-11-21 06:18

    In the following I assume you are only interested in why Object.create is preferable for setting up inheritance.

    To understand the benefits, lets first clarify what a "class" is made of in JavaScript. You have two parts:

    1. The constructor function. This function contains all the logic to create an instance of the "class", i.e. instance specific code.

    2. The prototype object. This is the object the instance inherits from. It contains all methods (and other properties) that should be shared among all instances.

    Inheritance establishes an is-a relation, for example, a Dog is an Animal. How is this expressed in terms of constructor function and prototype object?

    Obviously a dog must have the same methods as an animal, that is the Dog prototype object must somehow incorporate the methods from the Animal prototype object. There are multiple ways to do this. You will often see this:

    Dog.prototype = new Animal();
    

    This works because an Animal instance inherits from the Animal prototype object. But it also implies that every dog inherits from one specific Animal instance. That seems to be a bit strange. Shouldn't instance specific code only be run in the constructor function? Suddenly instance specific code and prototype methods seem to be mixed.

    We don't actually want to run Animal instance specific code at that moment, we only want all the methods from the Animal prototype object. That is what Object.create lets us do:

    Dog.prototype = Object.create(Animal.prototype);
    

    Here we are not creating a new Animal instance, we only get the prototype methods. The instance specific code is executed exactly where it should be, inside the constructor:

    function Dog() { 
       Animal.call(this, 'Dog'); 
    }
    

    The biggest advantage is that Object.create will always work. Using new Animal() only works if the constructor does not expect any arguments. Imagine if the constructor looked like this:

    function Animal(name) { 
        this.name = name.toLowerCase();
    }
    

    You always have to pass a string to Animal, otherwise you will get an error. What will you pass when you do Dog.prototype = new Animal(??);? It doesn't actually matter which string you pass, as long as pass something, which hopefully shows you that this is bad design.


    Some say that Dog.prototype = Animal.prototype; can also work. So now I'm totally confused

    Everything that "adds" the properties from Animal.prototype to Dog.prototype will "work". But the solutions are of different quality. In this case here you will have the problem that any method you add to Dog.prototype will also be added to Animal.prototype.

    Example:

    Dog.prototype.bark = function() {
        alert('bark');
    };
    

    Since Dog.prototype === Animal.prototype, all Animal instances have a method bark now, which is certainly not what you want.

    Object.create (and even new Animal) add one level of indirection to the inheritance by creating a new object which inherits from Animal.prototype and that new object becomes Dog.prototype.


    Inheritance in ES6

    ES6 introduces a new syntax to create constructor functions and prototype methods, which looks like this:

    class Dog extends Animal {
    
      bark() {
        alert('bark');
      }
    
    }
    

    This is more convenient than what I explained above, but as it turns out, extends also uses an internal equivalent to Object.create to setup inheritance. See steps 2 and 3 in the ES6 draft.
    Which means that using Object.create(SuperClass.prototype) is the "more correct" approach in ES5.

    0 讨论(0)
  • 2020-11-21 06:20

    First, running the Animal constructor may have undesired side effects. Consider this:

    var Animal = function(name) {
        this.name = name;
        Animal.instances.push(this);
    };
    Animal.instances = [];
    

    This version would keep track of all instances that have been created. You don't want your Dog.prototype to be recorded there.

    Second, Dog.prototype = Animal.prototype is a bad idea, since that would mean that bark would become a method of Animal.

    0 讨论(0)
  • 2020-11-21 06:38

    Let's understand it with code only;

    A.prototype = B.prototype;

    function B() {console.log("I am B");this.b1= 30;}
        B.prototype.b2 = 40;
    
        function A() {console.log("I am A");this.a1= 10;}
        A.prototype.a2 = 20;
    
        A.prototype = B.prototype;
    
        A.prototype.constructor = A; 
    
        var a = new A;
        var b = new B;
    
        console.log(a);//A {a1: 10, b2: 40}
        console.log(b);//B {b1: 30, b2: 40}
    
        console.log(A.prototype.constructor);//A
        console.log(B.prototype.constructor);//A
        console.log(A.prototype);//A {b2: 40}
        console.log(B.prototype);//A {b2: 40}
        console.log(a.constructor === A); //true
        console.log(b.constructor === A); //true
    
    console.log(a.a2);//undefined
    

    enter image description here

    A.prototype = Object.create(B.prototype);

    function B() {console.log("I am B");this.b1= 30;}
    B.prototype.b2 = 40;
    
    function A() {console.log("I am A");this.a1= 10;}
    A.prototype.a2 = 20;
    
    A.prototype = Object.create(B.prototype);
    
    A.prototype.constructor = A; 
    
    var a = new A;
    var b = new B;
    
    console.log(a);//A {a1: 10, constructor: function, b2: 40}
    console.log(b);//B {b1: 30, b2: 40} 
    
    console.log(A.prototype.constructor);//A
    console.log(B.prototype.constructor);//B
    console.log(A.prototype);//A {constructor: function, b2: 40}
    console.log(B.prototype);//B {b2: 40}
    console.log(a.constructor === A); //true
    console.log(b.constructor === B); //true
    console.log(a.a2);//undefined
    

    enter image description here

    0 讨论(0)
提交回复
热议问题