Javascript原型链的副作用

时间:2017-11-30 09:54:35

标签: javascript prototype

这个问题与书中的例子有关。我试图理解它,但在某些时候它让我很困惑。 在此示例中,我们使用一个属性function Ninja()创建了一个构造函数swung。 我们创建了一个名为ninja1的构造函数的对象实例。 然后我们将一个方法添加到Ninja()构造函数的原型中,其名称为swingSword()。 现在,如果我们检查ninja1.swingSword(),我们可以访问该方法,一切都很好。

现在我们开始,我们为Ninja()设置一个新的原型对象,并设置一个返回true的方法pierce()

到目前为止我注意到了两件事。首先,ninja1.swingSword()仍然可以访问其“旧”原型。其次是来自Ninja()构造函数的新创建的对象无法访问swingSword(),但是他们可以访问.pierce()方法。 我的问题是,为什么ninja1对象无法访问pierce()方法?仍然与其原型连接到其构造函数Ninja()。我发布的代码是为了清楚说明。

function Ninja() {
  this.swung = true;
}

const ninja1 = new Ninja();

Ninja.prototype.swingSword = function() {
  return this.swung;
}

console.log(ninja1.swingSword());

Ninja.prototype = {
  pierce: function() {
    return true;
  }
}

console.log('ninj1 object can still use the old prototype even replacing old prototype with new object :' + ninja1.swingSword());


const ninja2 = new Ninja();

console.log(ninja2.pierce());
console.log(ninja1.pierce());

3 个答案:

答案 0 :(得分:4)

您已更改整个prototype对象,因此它现在只有pierce功能。但是之前定义的对象 - ninja1仍然引用了之前的prototype

Ninja.prototype ---> |--------------|
                     | Swing Swords | 
                     |--------------|

                           ^
                           | prototype

ninja1 ------------> |its properties|

当您更改原型时,它现在看起来像

                     |--------------|
                     | Swing Swords | 
                     |--------------|

                           ^
                           | prototype

ninja1 ------------> |its properties| 


Ninja.prototype ---> |--------|
                     | pierce | 
                     |--------|

                          ^
                          | prototype

ninja2 ----------> |its properties|

您可以看到ninja1仍然包含对旧原型对象的引用,并且与新原型没有任何关系。但ninja2必须使用较新的原型对象。

答案 1 :(得分:0)

当您创建构造函数的实例时,会在该对象中创建 proto 链接,该链接引用构造函数的原型,因此在原型上添加任何属性或方法都不会发生变化链接或引用但是当你定义一个新的原型时,构造函数将有一个原型对象的新引用,因此在更改之后创建的实例将引用新原型,但较旧的原型仍然将旧对象引用为 proto < / strong>仍然是指旧原型对象的链接。

答案 2 :(得分:0)

可能有助于准确理解幕后发生的事情。以下是使用new关键字调用函数时发生的情况,并使用伪代码完成:

  1. 创建一个新对象,其属性在创建时将其链接到构造函数和构造函数的prototype属性

    var newObj = {
        constructor: Ninja,
        __proto__: Ninja.prototype // = { swingSword: function() { ... } }
    };
    
  2. 调用构造函数时,将this上下文分配给新对象并传递任何参数。

    var returned = Ninja.call(newObj, ...arguments); // In your case, no arguments
    
  3. 如果构造函数返回undefined,则绑定到this的新对象将成为实例值 - 包含您在构造函数中添加的任何属性。

    var instance = typeof returned === 'undefined' ? newObj : returned;
    
  4. 第一次调用new Ninja()时,会返回一个引用原始值__proto__的{​​{1}}属性的对象。这个Ninja.prototype属性处理原型链。 (见下面的解释。)

    当您更改__proto__的值时,您刚刚创建的对象仍然引用旧值作为其原型;其Ninja.prototype属性未发生变化。它仍然会委托旧原型上的属性。但是,如果再次调用__proto__,则会得到一个new Ninja()属性引用更新原型的对象。

    旁注:什么是__proto__

    __proto__有点顽皮。

    EcmaScript规范规定每个对象都应具有__proto__属性,该属性是对其原型的引用。

    奇怪的名称来自于它是规范属性 - 规范为方便起见而定义的东西,但实际的实现并不需要公开。它仅作为规范的紧凑方式存在,以描述原型链的工作原理。

    但是,早期的JS引擎开始将对[[Prototype]]内部[[Prototype]]属性的访问权限公开为__proto__。这种做法变得非常普遍,ES2015规范现在明确定义了它,虽然标记为遗留特征。