Eloquent Javascript 栩栩如生的 Terrarium 克隆
Eloquent Javascript LifeLikeTerrarium clone
我正在阅读 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,此代码正试图实现这一点。我不确定它是如何实现的。
- 'Terrarium.call(this, plan) 是否将数据传递给 Terrarium 构造函数,就像您在继承的上下文中调用了 Terrarium(plan) 一样?
- 调用clone(object)的目的是什么?这与说“LifeLikeTerrarium.prototype = Terrarium.prototype?
有何不同
- 将 LifeLikeTerrarium 的构造函数分配给自己的目的是什么?
我觉得我理解了其余的代码,但这似乎是一个非常关键的部分。真的很感激任何愿意为我分解它的人。
谢谢!
Does doing the Terrarium.call(this, plan)
pass the data to the Terrarium
constructor as if you had called Terrarium(plan)
within the context of the inheritence?
是的。 Function#call
使用给定的 this
值和单独提供的参数调用函数。这意味着我们将被调用函数内的 this
指定为我们想要的任何对象。 Terrarium.call(this, plan)
将我们当前的 this
(这将是 LifeLikeTerrarium
的一个实例)传递给对 Terrarium
的调用。如果 Terrarium
在 itslef 内部赋值或修改 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 saying LifeLikeTerrarium.prototype = Terrarium.prototype
?
我们不想做 LifeLikeTerrarium.prototype = Terrarium.prototype
。曾经。因为 LifeLikeTerrarium
很可能也会有自己的方法 到 Terrarium
。想象一下,我们向 LifeLikeTerrarium
的原型添加一个名为 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
,这样两个原型都引用同一个对象。不,我们分配给 LifeLikeTerrarium.prototype
一个普通的新对象,其原型设置为 Terrarium.prototype
。新的普通对象(这是 new OneShotConstructor()
的结果)将充当 LifeLikeTerrarium
自己的原型。该普通对象将其原型设置为 Terrarium.prototype
,这导致 Terrarium
的方法可用于 LifeLikeTerrarium
,这种现象被称为 "Inheritance"。下面是两种方式的区别图:
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
的原型引用了 Terrarium.prototype
(由于 OneShotConstructor.prototype = object;
而成为可能)。这创建了一个很好的原型链,让 Terrarium
的方法可用于 LifeLikeTerrarium
如果后者不想拥有自己的方法,如果它想要(想要拥有自己的),那将是完全没问题,因为它们不会取代 Terrarium
,它们只会隐藏它们。这就是 prototype inheritence.
的本质
What is the purpose of assigning LifeLikeTerrarium
's constructor to itself?
这里已经有了答案:Why is it necessary to set the prototype constructor?
我正在阅读 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,此代码正试图实现这一点。我不确定它是如何实现的。
- 'Terrarium.call(this, plan) 是否将数据传递给 Terrarium 构造函数,就像您在继承的上下文中调用了 Terrarium(plan) 一样?
- 调用clone(object)的目的是什么?这与说“LifeLikeTerrarium.prototype = Terrarium.prototype? 有何不同
- 将 LifeLikeTerrarium 的构造函数分配给自己的目的是什么?
我觉得我理解了其余的代码,但这似乎是一个非常关键的部分。真的很感激任何愿意为我分解它的人。
谢谢!
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
在 itslef 内部赋值或修改 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
很可能也会有自己的方法 Terrarium
。想象一下,我们向 LifeLikeTerrarium
的原型添加一个名为 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
,这样两个原型都引用同一个对象。不,我们分配给 LifeLikeTerrarium.prototype
一个普通的新对象,其原型设置为 Terrarium.prototype
。新的普通对象(这是 new OneShotConstructor()
的结果)将充当 LifeLikeTerrarium
自己的原型。该普通对象将其原型设置为 Terrarium.prototype
,这导致 Terrarium
的方法可用于 LifeLikeTerrarium
,这种现象被称为 "Inheritance"。下面是两种方式的区别图:
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
的原型引用了 Terrarium.prototype
(由于 OneShotConstructor.prototype = object;
而成为可能)。这创建了一个很好的原型链,让 Terrarium
的方法可用于 LifeLikeTerrarium
如果后者不想拥有自己的方法,如果它想要(想要拥有自己的),那将是完全没问题,因为它们不会取代 Terrarium
,它们只会隐藏它们。这就是 prototype inheritence.
What is the purpose of assigning
LifeLikeTerrarium
's constructor to itself?
这里已经有了答案:Why is it necessary to set the prototype constructor?