使用 `Object.create` 进行继承的好处
我一直在尝试理解 ECMAScript 5 中引入的新 Object.create
方法.
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 something like this:
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'); }
我只是将一个新创建的 Animal 对象分配给 Dog 的原型,一切都像魅力一样工作:
I just assign a newly created Animal object to Dog's prototype and everything works like a charm:
var dog1 = new Dog();
dog1.print(); // prints 'Dog'
dog1.bark(); // prints 'bark'
dog1.name; //prints 'Dog'
但是人们(没有解释)说 Dog.prototype = new Animal();
不是继承的工作方式,我应该使用 Object.create 方法:
but people(without explaining) are saying that Dog.prototype = new Animal();
is not the way inheritance works and that I should use Object.create approach:
Dog.prototype = Object.create(Animal.prototype);
这也有效.
使用 Object.create
有什么好处还是我遗漏了什么?
What's the benefit of using Object.create
or am I missing something?
更新:有人说 Dog.prototype = Animal.prototype;
也可以.所以现在我完全糊涂了
UPDATE: Some say that Dog.prototype = Animal.prototype;
can also work. So now I'm totally confused
推荐答案
下面我假设您只对为什么 Object.create
更适合设置继承感兴趣.
In the following I assume you are only interested in why Object.create
is preferable for setting up inheritance.
要了解它的好处,让我们首先澄清一下 JavaScript 中的类"是由什么组成的.你有两个部分:
To understand the benefits, lets first clarify what a "class" is made of in JavaScript. You have two parts:
构造函数函数.该函数包含创建类"实例的所有逻辑,即特定于实例的代码.
The constructor function. This function contains all the logic to create an instance of the "class", i.e. instance specific code.
原型对象.这是实例继承的对象.它包含应在所有实例之间共享的所有方法(和其他属性).
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.
继承建立is-a关系,例如,Dog
isAnimal代码>.这如何用构造函数和原型对象表示?
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?
很明显,狗必须具有与动物相同的方法,即 Dog
prototype 对象必须以某种方式合并来自 Animal
的方法原型 对象.有多种方法可以做到这一点.你会经常看到这样的:
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();
这是因为 Animal
instance 继承自 Animal
prototype 对象.但是这也意味着每只狗都继承自一个特定的Animal
实例.这似乎有点奇怪.实例特定代码不应该只在 constructor 函数中运行吗?突然,instance 特定代码和 prototype 方法似乎混在一起了.
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.
此时我们实际上并不想运行 Animal
instance 特定的代码,我们只想要 Animal
中的所有方法 原型对象.这就是 Object.create
让我们做的:
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);
这里我们不是创建一个新的Animal
实例,我们只得到原型方法.instance 特定的代码在构造函数内部准确地执行:
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');
}
最大的优点是 Object.create
将始终 工作.new Animal()
仅在构造函数不期望任何参数时才有效.想象一下,如果构造函数看起来像这样:
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();
}
你总是必须将一个字符串传递给Animal
,否则你会得到一个错误.当你执行 Dog.prototype = new Animal(??);
时你会传递什么?实际上,你传递哪个字符串并不重要,只要传递 something,希望这能告诉你这是一个糟糕的设计.
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.
有人说Dog.prototype = Animal.prototype;
也可以.所以现在我完全糊涂了
Some say that
Dog.prototype = Animal.prototype;
can also work. So now I'm totally confused
所有将 Animal.prototype
属性添加"到 Dog.prototype
的东西都将起作用".但是解决方案的质量不同.在这种情况下,您将遇到添加到 Dog.prototype
的任何方法也将添加到 Animal.prototype
的问题.
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
.
例子:
Dog.prototype.bark = function() {
alert('bark');
};
由于Dog.prototype === Animal.prototype
,现在所有的Animal
实例都有一个方法bark
,这肯定不是你的想.
Since Dog.prototype === Animal.prototype
, all Animal
instances have a method bark
now, which is certainly not what you want.
Object.create
(甚至是 new Animal
)通过创建一个继承自 Animal.prototype的新对象来为继承添加一层间接性code> 并且该新对象变为
Dog.prototype
.
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
.
ES6 中的继承
ES6 引入了一种新语法来创建构造函数和原型方法,如下所示:
ES6 introduces a new syntax to create constructor functions and prototype methods, which looks like this:
class Dog extends Animal {
bark() {
alert('bark');
}
}
这比我上面解释的更方便,但事实证明,extends
还使用与 Object.create
等效的内部来设置继承.请参阅 ES6 草案中的步骤 2 和 3.
这意味着使用 Object.create(SuperClass.prototype)
是 ES5 中更正确"的方法.
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.
相关文章