javascript - Eloquent Javascript LifeLikeTerrarium 克隆

标签 javascript inheritance 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赋值或修改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很可能会有自己的方法 we don't want addedTerrarium也是。假设我们向 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.prototypeLifeLikeTerrarium.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.prototypeLifeLikeTerrarium.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/

    相关文章:

    JavaScript:使用 .exec() 查找连续匹配项

    javascript - 在 jQuery 切片后重新 append 列表项并删除

    c++ - 混合基类的虚拟和非虚拟继承

    javascript - 复制对象数组哪种方式更快: slice or clone?

    javascript - 使用 HTML/CSS 中的文本创建缩略图

    inheritance - 修复用例图 : actor boundaries and generalization

    c# - 如何在派生类中调用基类构造函数?

    git - 如何使用git只获取提交历史,而不是文件

    Jquery 克隆输入并删除克隆输入中的值

    Javascript 变量不保留值