使用Object.createinheritance的好处

我一直在围绕在ECMAScript 5中引入的新的Object.create方法。

通常当我想使用inheritance,我做这样的事情:

 var Animal = function(name) { this.name = name; } Animal.prototype.print = function() { console.log(this.name); } var Dog = function() { return Animal.call(this, 'Dog'); } Dog.prototype = new Animal(); Dog.prototype.bark = function() { console.log('bark'); } 

我只是将一个新创build的动物物体分配给狗的原型,一切都像一个魅力:

 var dog1 = new Dog(); dog1.print(); // prints 'Dog' dog1.bark(); // prints 'bark' dog1.name; //prints 'Dog' 

但是人们(没有解释)在说Dog.prototype = new Animal(); 不是inheritance的工作方式,我应该使用Object.create方法:

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

这也有效。

使用Object.create什么好处,或者我错过了什么?

更新:有人说, Dog.prototype = Animal.prototype; 也可以工作。 所以现在我完全困惑

在下面,我假设你只关心为什么Object.create更适合于设置inheritance。

要理解这些好处,首先要明确一下JavaScript中的“类”是什么。 你有两个部分:

  1. 构造函数 这个函数包含所有创build“类”实例的逻辑,即实例特定的代码。

  2. 原型对象。 这是实例inheritance的对象。 它包含所有应该在所有实例之间共享的方法(和其他属性)。

inheritancebuild立了一个关系,例如,一只Dog 一只Animal 。 这是如何用构造函数和原型对象表示的?

显然,狗必须和动物有相同的方法,那就是Dog 原型对象必须以某种方式包含Animal 原型对象的方法。 有多种方法可以做到这一点。 你会经常看到这个:

 Dog.prototype = new Animal(); 

这是可行的,因为Animal 实例inheritance了Animal 原型对象。 但这也意味着每只狗都从一个特定的Animal 实例inheritance。 这似乎有点奇怪。 不应该在构造函数中只运行实例特定的代码? 突然出现的实例特定的代码和原型方法似乎是混合的。

我们实际上并不想运行Animal 实例特定的代码,我们只需要Animal 原型对象的所有方法。 这就是Object.create让我们做的事情:

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

在这里我们不是创build一个新的Animal实例,我们只获取原型方法。 特定于实例的代码在构造函数内部正确执行:

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

最大的优点是Object.create始终工作。 使用new Animal()只适用于构造函数不期望任何参数。 想象一下,如果构造函数看起来像这样:

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

你总是要传递一个string给Animal ,否则你会得到一个错误。 当你做Dog.prototype = new Animal(??); ? 实际上,只要传递一些东西 ,希望告诉你这是糟糕的devise,那么你传递哪一个string并不重要。


有人说, Dog.prototype = Animal.prototype; 也可以工作。 所以现在我完全困惑

Animal.prototypeDog.prototype的属性“添加”的所有内容都将“起作用”。 但是解决scheme的质量不同。 在这种情况下,您将遇到以下问题:您添加到Dog.prototype任何方法也将被添加到Animal.prototype

例:

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

由于Dog.prototype === Animal.prototype ,现在所有的Animal实例都有一个方法叫,这当然不是你想要的。

Object.create (甚至是new Animal )通过创build一个inheritance自Animal.prototype的新对象,并且新对象变成Dog.prototype ,为inheritance添加一个间接级别。


ES6中的inheritance

ES6引入了一个新的语法来创build构造函数和原型方法,如下所示:

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

这比我上面解释的更方便,但是事实certificate, extends还使用了一个与Object.create相当的内部来设置inheritance。 请参阅ES6草案中的步骤2和3。
这意味着使用Object.create(SuperClass.prototype)是ES5中“更正确”的方法。

首先,运行Animal构造函数可能会产生不希望的副作用。 考虑这个:

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

该版本将跟踪所有已创build的实例。 你不希望你的Dog.prototype被logging在那里。

其次, Dog.prototype = Animal.prototype是一个坏主意,因为那意味着bark会成为Animal一种方法。

我试图说明一下这个区别:

这是基本上当你写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; 

这里是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; 

所以它不会调用Animal函数,它也总是返回新创build的对象。 在你的情况下,你最终有两个不同的对象。 用第一种方法你得到:

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

用第二种方法得到:

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

你真的不需要在你的原型中有name属性,因为你已经用Animal.call(this, 'Dog');把它分配给你的Dog实例Animal.call(this, 'Dog');

您的主要目标是让您的Dog实例访问Animal原型的所有属性,这是通过两种方法实现的。 然而,第一种方法会做一些额外的事情,在您的情况下并不是真正需要的,或者甚至可能会导致Pumbaa80提到的不需要的结果。

让我们只用代码来理解它;

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 

在这里输入图像描述

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 

在这里输入图像描述