我正在阅读 Marijn Haverbeke 的 Eloquent Javascript,并且在理解 LifeLikeTerrarium 示例的一部分时遇到了一些麻烦。具体如下代码:
function clone(object){
function OneShotConstructor(){}
OneShotConstructor.prototype = object;
return new OneShotConstructor();
}
LifeLikeTerrarium 的构造函数调用它:
function LifeLikeTerrarium(plan){
Terrarium.call(this, plan);
}
LifeLikeTerrarium.prototype = clone(Terrarium.prototype);
LifeLikeTerrarium.prototype.constructor = LifeLikeTerrarium;
我的理解是 LifeLikeTerrarium 应该从 Terrarium 继承,而这段代码正试图实现这一点。我不确定它是如何实现这一目标的。
我觉得我理解了其余的代码,但这似乎是一个非常关键的部分。非常感谢任何愿意为我分解它的人。
谢谢!
最佳答案
Does doing the
Terrarium.call(this, plan)
pass the data to theTerrarium
constructor as if you had calledTerrarium(plan)
within the context of the inheritence?
是的。
Function#call
使用给定的 this
调用函数值和参数单独提供。这意味着我们指定 this
在被调用函数内部成为我们想要的任何对象。 Terrarium.call(this, plan)
通过我们目前的this
(这将是 LifeLikeTerrarium
的一个实例)到对 Terrarium
的调用.如果 Terrarium
赋值或修改this
在其内部,this
它修改将是我们使用 call
传递给它的内容,这里是 LifeLikeTerrarium
的当前实例.其余参数为call
都作为单独的参数传递给被调用的函数。演示:
function foo(a, b) {
this.sum = a + b;
}
var obj = {};
foo.call(obj, 5, 7); // obj will be the 'this' used inside foo.
// 5 and 7 will be the values of foo's arguments a and b, respectively
console.log(obj); // et voilà
What is the purpose of calling
clone(object)
? How is this different from sayingLifeLikeTerrarium.prototype = Terrarium.prototype
?
我们不想做
LifeLikeTerrarium.prototype = Terrarium.prototype
.曾经。因为LifeLikeTerrarium
很可能会有自己的方法 we don't want added至Terrarium
也是。假设我们向 LifeLikeTerrarium
添加一个方法的原型(prototype)称为 foo
. foo
也将提供给 Terrarium
.更糟糕的是,foo
可以替换 Terrarium
自己的方法foo
,如果它恰好有一个:function Terrarium() { }
Terrarium.prototype.sayHi = function() {
console.log("Hi! I'm Terrarium");
}
function LifeLikeTerrarium() { }
LifeLikeTerrarium.prototype = Terrarium.prototype; // now LifeLikeTerrarium.prototype and Terrarium.prototype are both referencing the same object, changes to one are reflected on the other
LifeLikeTerrarium.prototype.sayHi = function() { // Terrarium.prototype.sayHi is gone for ever. May it rest in peace.
console.log("Hi! I'm LifeLikeTerrarium");
}
var terrarium = new Terrarium();
terrarium.sayHi(); // wrong wrong wrong
如您所见,这非常糟糕。根据方法的作用,它甚至可能抛出错误并中断执行(例如,如果新方法使用原始对象中没有的属性
Terrarium
):function Terrarium() { }
Terrarium.prototype.sayHi = function() {
console.log("Hi! I'm an abstract Terrarium that doesn't have a name");
}
function LifeLikeTerrarium(name) {
this.name = name;
}
LifeLikeTerrarium.prototype = Terrarium.prototype;
LifeLikeTerrarium.prototype.sayHi = function() {
console.log("Hi! I'm " + this.name.toUpperCase()); // b.. b.. but Terrarium doen't have a property called name. Let's see what will happen. Maybe it'll work
}
var terrarium = new Terrarium();
terrarium.sayHi(); // Surprise! You got mail, I mean an error
除此之外,我们至少会用它甚至不需要或使用的方法来污染原始对象:
function Terrarium() { }
function LifeLikeTerrarium() { }
LifeLikeTerrarium.prototype = Terrarium.prototype;
LifeLikeTerrarium.prototype.sayHi = function() { // now Terrarium has a method called sayHi although it originally didn't have one.
console.log("Hi!");
}
var terrarium = new Terrarium();
terrarium.sayHi(); // Want proof? Here you go.
我们如何解决这个问题?答案是:我们不分配
Terrarium.prototype
至LifeLikeTerrarium.prototype
这样两个原型(prototype)都引用同一个对象。不,我们分配给 LifeLikeTerrarium.prototype
一个普通的新对象,其原型(prototype)设置为 Terrarium.prototype
.新的普通对象(new OneShotConstructor()
的结果)将充当 LifeLikeTerrarium
自己的原型(prototype)。该普通对象将其原型(prototype)设置为 Terrarium.prototype
导致 Terrarium
的方法可用于 LifeLikeTerrarium
,一种被称为“继承”的现象。下面是两种方式的区别示意图:LifeLikeTerrarium.prototype = Terrarium.prototype
: Terrarium.prototype ---------------------> {
| ...
| ...
| }
|
LifeLikeTerrarium.prototype ---/
LifeLikeTerrarium.prototype = clone(Terrarium.prototype)
: Terrarium.prototype ---------------------> {
| ...
| ...
| }
|
\------------------------------\
|
LifeLikeTerrarium.prototype -------------> { |
... |
... |
prototype: ---/
}
如您所见,在第一张图中,
Terrarium.prototype
和 LifeLikeTerrarium.prototype
引用同一个对象。而在第二张图中,每个都引用了自己的对象:Terrarium.prototype
正在引用它自己的对象,该对象在您的代码和 LifeLikeTerrarium.prototype
的某处定义正在引用 OneShotConstructor
的实例(这是一个空对象,因为构造函数是一个空的)。另一个好处是 LifeLikeTerrarium.prototype
的原型(prototype)引用 Terrarium.prototype
(由于 OneShotConstructor.prototype = object;
而成为可能)。这创建了一个很好的原型(prototype)链,它允许 Terrarium
的方法可联系 LifeLikeTerrarium
如果后者不想拥有自己的,并且如果确实(想要拥有自己的),那将完全没问题,因为它们不会替换 Terrarium
's,他们只会遮蔽他们。这就是 prototype inheritence 的精髓.What is the purpose of assigning
LifeLikeTerrarium
's constructor to itself?
这已经有答案了:Why is it necessary to set the prototype constructor?
关于javascript - Eloquent Javascript LifeLikeTerrarium 克隆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50892169/