我一直试图围绕Object.createECMAScript 5中引入的新方法.
通常,当我想使用继承时,我会这样做:
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'); }
Run Code Online (Sandbox Code Playgroud)
我只是将一个新创建的Animal对象分配给Dog的原型,一切都像魅力:
var dog1 = new Dog();
dog1.print(); // prints 'Dog'
dog1.bark(); // prints 'bark'
dog1.name; //prints 'Dog'
Run Code Online (Sandbox Code Playgroud)
但人们(没有解释)说这Dog.prototype = new Animal();不是继承的工作方式,我应该使用Object.create方法:
Dog.prototype = Object.create(Animal.prototype);
Run Code Online (Sandbox Code Playgroud)
这也有效.
使用的好处是什么,Object.create或者我错过了什么?
更新:有人说这Dog.prototype = Animal.prototype;也可以.所以现在我完全糊涂了
Fel*_*ing 113
在下面我假设您只关心为什么Object.create更适合设置继承.
要了解这些好处,首先要弄清楚JavaScript中的"类"是什么.你有两个部分:
该构造函数.此函数包含创建"类"实例的所有逻辑,即特定于实例的代码.
该原型对象.这是实例继承的对象.它包含应在所有实例之间共享的所有方法(和其他属性).
继承建立了一个is-a关系,例如,a Dog 是一个Animal.这是如何用构造函数和原型对象表达的?
显然,狗必须有相同的方法作为一种动物,那就是Dog 原型对象必须以某种方式合并从方法的Animal 原型对象.有多种方法可以做到这一点.你会经常看到这个:
Dog.prototype = new Animal();
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为Animal 实例继承自Animal 原型对象.但它也意味着每只狗都从一个特定的Animal 实例继承.这似乎有点奇怪.不应该在构造函数中运行实例特定代码吗?突然间,实例特定的代码和原型方法似乎是混合的.
我们实际上并不想在那一刻运行特定于Animal 实例的代码,我们只想要Animal 原型对象中的所有方法.这就是Object.create让我们做的事情:
Dog.prototype = Object.create(Animal.prototype);
Run Code Online (Sandbox Code Playgroud)
这里我们不是创建一个新Animal实例,我们只获取原型方法.该实例特定代码究竟执行的地方应该是,在构造函数中:
function Dog() {
Animal.call(this, 'Dog');
}
Run Code Online (Sandbox Code Playgroud)
最大的好处是,Object.create将始终工作.new Animal()仅在构造函数不期望任何参数时才使用.想象一下,如果构造函数看起来像这样:
function Animal(name) {
this.name = name.toLowerCase();
}
Run Code Online (Sandbox Code Playgroud)
您总是必须传递一个字符串Animal,否则您将收到错误.你什么时候通过Dog.prototype = new Animal(??);?你传递的字符串实际上并不重要,只要传递一些东西,这有希望告诉你这是一个糟糕的设计.
有人说也
Dog.prototype = Animal.prototype;可以.所以现在我完全糊涂了
一切,补充说:""从属性Animal.prototype,以Dog.prototype将"工作".但解决方案的质量不同.在这种情况下,您将遇到的问题是,您添加的任何方法Dog.prototype也将被添加到Animal.prototype.
例:
Dog.prototype.bark = function() {
alert('bark');
};
Run Code Online (Sandbox Code Playgroud)
因为Dog.prototype === Animal.prototype,所有Animal实例bark现在都有一个方法,这当然不是你想要的.
Object.create(甚至new Animal)通过创建新的对象,其从继承一个间接层添加到继承Animal.prototype和新对象成为Dog.prototype.
ES6中的继承
ES6引入了一种新语法来创建构造函数和原型方法,如下所示:
class Dog extends Animal {
bark() {
alert('bark');
}
}
Run Code Online (Sandbox Code Playgroud)
这比我上面解释的更方便,但事实证明,它extends也使用内部等效Object.create来设置继承.请参阅ES6草案中的步骤2和3 .
这意味着使用Object.create(SuperClass.prototype)是ES5中"更正确"的方法.
首先,运行Animal构造函数可能会产生不希望的副作用.考虑一下:
var Animal = function(name) {
this.name = name;
Animal.instances.push(this);
};
Animal.instances = [];
Run Code Online (Sandbox Code Playgroud)
此版本将跟踪已创建的所有实例.你不希望你Dog.prototype被录制在那里.
其次,这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;
Run Code Online (Sandbox Code Playgroud)
这是基本上发生的事情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;
Run Code Online (Sandbox Code Playgroud)
所以它做的相同,但它不调用Animal函数,它也总是返回新创建的对象.在您的情况下,您最终得到两个不同的对象.使用第一种方法:
Dog.prototype = {
name: undefined,
__proto__: Animal.prototype
};
Run Code Online (Sandbox Code Playgroud)
并使用第二种方法:
Dog.prototype = {
__proto__: Animal.prototype
};
Run Code Online (Sandbox Code Playgroud)
您并不需要name在原型中拥有该属性,因为您已经将它分配给您的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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

| 归档时间: |
|
| 查看次数: |
4779 次 |
| 最近记录: |