Javascript 为继承使用“Object.create”的好处
我一直在尝试用ECMAScript 5中引入的新Javascript 为继承使用“Object.create”的好处,javascript,Javascript,我一直在尝试用ECMAScript 5中引入的新对象.create方法来概括我的想法 通常,当我想使用继承时,我会这样做: var Animal = function(name) { this.name = name; } Animal.prototype.print = function() { console.log(this.name); } var Dog = function() { return Animal.call(this, 'Dog'); } Dog.proto
对象.create
方法来概括我的想法
通常,当我想使用继承时,我会这样做:
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'); }
function Animal(name) {
this.name = name.toLowerCase();
}
class Dog extends Animal {
bark() {
alert('bark');
}
}
我只是给狗的原型分配了一个新创建的动物对象,一切都像一个符咒:
var dog1 = new Dog();
dog1.print(); // prints 'Dog'
dog1.bark(); // prints 'bark'
dog1.name; //prints 'Dog'
但是人们(没有解释)说Dog.prototype=新动物()代码>不是继承的工作方式,我应该使用Object.create方法:
Dog.prototype = Object.create(Animal.prototype);
这同样有效
使用Object.create
有什么好处?还是我遗漏了什么
更新:有人说Dog.prototype=Animal.prototype代码>也可以工作。所以现在我完全困惑了
首先,运行Animal
构造函数可能会有不希望的副作用。考虑这一点:
var Animal = function(name) {
this.name = name;
Animal.instances.push(this);
};
Animal.instances = [];
此版本将跟踪已创建的所有实例。你不想把你的狗的原型记录在那里
其次,Dog.prototype=Animal.prototype
是一个坏主意,因为这意味着bark
将成为Animal
的一种方法。在下面的例子中,我假设您只对对象的原因感兴趣。创建是设置继承的首选方法
为了理解这些好处,让我们首先澄清JavaScript中的“类”是由什么组成的。你有两个部分:
构造函数函数。此函数包含创建“类”实例的所有逻辑,即实例特定代码
原型对象。这是实例从中继承的对象。它包含应在所有实例之间共享的所有方法(和其他属性)
继承建立is-a关系,例如,狗
是动物
。这是如何用构造函数和原型对象表示的
显然,狗必须具有与动物相同的方法,即狗
原型对象必须以某种方式合并动物
原型对象的方法。有多种方法可以做到这一点。你会经常看到:
Dog.prototype = new Animal();
这是因为Animal
实例继承自Animal
prototype对象但这也意味着每只狗都从一个特定的动物
实例继承。这似乎有点奇怪。实例特定的代码不应该只在构造函数中运行吗?突然间,特定于实例的代码和原型方法似乎混合在一起了
我们实际上不想在那一刻运行特定于实例的Animal
代码,我们只需要Animal
prototype对象中的所有方法。这就是对象。创建让我们做:
Dog.prototype = Object.create(Animal.prototype);
这里我们不是创建一个新的Animal
实例,我们只得到原型方法。特定于实例的代码在构造函数内部的正确位置执行:
function Dog() {
Animal.call(this, 'Dog');
}
最大的优点是Object.create
始终有效。仅当构造函数不需要任何参数时,使用new Animal()
才有效。想象一下,如果构造函数看起来像这样:
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'); }
function Animal(name) {
this.name = name.toLowerCase();
}
class Dog extends Animal {
bark() {
alert('bark');
}
}
您必须始终将字符串传递给Animal
,否则将出现错误。当你做Dog.prototype=新动物(?)时,你会通过什么代码>?实际上,传递哪个字符串并不重要,只要传递一些东西就行,希望这能向您表明这是一个糟糕的设计
有人说,Dog.prototype=Animal.prototype代码>也可以工作。所以现在我完全糊涂了
从Animal.prototype
到Dog.prototype
中“添加”属性的所有内容都将“起作用”。但解决方案的质量不同。在本例中,您将遇到一个问题,即添加到Dog.prototype
的任何方法也将添加到Animal.prototype
例如:
Dog.prototype.bark = function() {
alert('bark');
};
由于Dog.prototype===Animal.prototype
,所有Animal
实例现在都有一个方法bark
,这肯定不是您想要的
Object.create
(甚至new Animal
)通过创建继承自Animal.prototype
的新对象,为继承添加一个间接层次,新对象将成为Dog.prototype
ES6中的继承
ES6引入了一种新语法来创建构造函数和原型方法,如下所示:
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'); }
function Animal(name) {
this.name = name.toLowerCase();
}
class Dog extends Animal {
bark() {
alert('bark');
}
}
这比我上面解释的更方便,但事实证明,extends
还使用了一个内部等价物Object.create
来设置继承。请参阅中的步骤2和3。
这意味着使用Object.create(SuperClass.prototype)
是ES5中“更正确”的方法。我试图稍微说明一下区别:
下面是编写新动物()
时的基本情况:
下面是对象的基本情况。创建:
//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
函数,而且它总是返回新创建的对象。
在你的例子中,你最终得到了两个不同的对象。第一种方法是:
Dog.prototype = {
name: undefined,
__proto__: Animal.prototype
};
Dog.prototype = {
__proto__: Animal.prototype
};
第二种方法是:
Dog.prototype = {
name: undefined,
__proto__: Animal.prototype
};
Dog.prototype = {
__proto__: Animal.prototype
};
您实际上不需要在原型中包含name
属性,因为您已经使用Animal.call将其分配给了Dog
实例(此为“Dog”)代码>
您的主要目标是让您的狗
实例访问动物
原型的所有属性,这两种方法都可以实现。然而,第一种方法做了一些在您的情况下并不真正需要的额外工作,甚至可能导致Pumbaa80提到的不想要的结果。让我们仅用代码来理解它
A。