【问题标题】:Javascript : Modifying Prototype doesn't affect existing Instances [duplicate]Javascript:修改原型不会影响现有实例 [重复]
【发布时间】:2017-10-01 13:52:13
【问题描述】:

我创建了 2 个原型实例,更改了原型中的一个函数,更改反映在两个实例中(很棒)。 但是,当我通过删除函数修改原型时,该函数对于现有实例仍然存在。

function A() {
  this.name = "cool";
}

A.prototype = {
  howCool: function() {
    return this.name + "er";
  }
};

var a1 = new A(),
  a2 = new A();

a1.name = "hot";
//line1
console.log(a1.howCool());
//line2
console.log(a2.howCool());

A.prototype = {};

//line3
console.log(a1.howCool());

//line4
var a3 = new A();
console.log(a3.howCool());

第 1 行和第 2 行按预期工作,将原型设置回空后,第 4 行显示未定义,这是预期的。 然而,第 3 行仍然显示函数定义。

【问题讨论】:

  • 您不是在修改原型对象,而是在创建一个新对象。
  • 您实际上将原型属性重新分配给了一个新对象。旧的仍然存在。
  • FWIW,它不能以这种方式工作的原因与为什么在var a = 42; var b = a; a = 21; 之后,b 仍然具有值42:JavaScript as copy/assign by value 语义相同。 b 具有 a 值的副本,而不是对变量 a 本身的引用。与prototype 类似,新实例获得A.prototype 的副本(这是对object 的引用),而不是对 的引用属性 A.prototype 本身。

标签: javascript ecmascript-6 prototype


【解决方案1】:

尝试使用delete 运算符

function A(){
    this.name = "cool";
}

A.prototype = {
    howCool : function(){
        return this.name + "er";
    }
};

var a1 = new A(),
    a2 = new A();

a1.name = "hot";
//line1
console.log(a1.howCool);
//line2
console.log(a2.howCool);

delete A.prototype.howCool;

//line3
console.log(a1.howCool);

//line4
var a3 = new A();
console.log(a3.howCool);

【讨论】:

  • A.prototype.howCool = undefined;
【解决方案2】:

如果你修改原型,它会影响旧实例,但如果你创建一个新的,它不会影响旧实例。

所以当你写的时候:

A.prototype = {};
//line3
console.log(a1.howCool);

对象a1.__proto__ 仍然指向初始的A.prototype,这就是它不记录undefined 的原因。

然后当你写的时候:

//line4
var a3 = new A();
console.log(a3.howCool());

新实例a3 正在使用新创建的A.prototype,这就是您得到ùndefined 的原因。

【讨论】:

    【解决方案3】:

    以下是可能的解释:
    (注意:以下解释仅用于实验和概念理解,因为在生产代码中应避免使用 __proto__)

    如您所知,对象(在本例中为 a1)和构造函数的原型(在本例中为 A)之间存在链接,该对象是从该原型中创建的。 此链接称为 __proto__ 或 dunder proto。所以在这种情况下,它可以被视为a1.__proto__

    现在,当你说A.prototype = {}; 时,此时对象 a1 已经被构造,它通过这个链接指向 A.prototype。

    由于 Javascript 是垃圾收集语言,仅将 A.prototype 设置为 {} 并不能立即清理旧的 protoytpe 对象,因此旧对象仍然存在。 所以当你说console.log(a1.howCool());的时候,howCool 就定位在了挥之不去的对象中。

    所以,如果你像这样修改你的 line3:

    //第3行

    console.log(a1.__proto__);
    a1.__proto__ = {};
    console.log(a1.howCool());
    

    您将能够在console.log(a1.__proto__); 行的控制台窗口中看到较旧的原型对象和其中的howCool() 但是,如果您设置了a1.__proto__ = {};,那么console.log(a1.howCool()); 将无法找到该对象,并且不会找到howCool()(因为它现在设置为空对象)。

    因此,仅将 A.prototype 设置为 {} 并不能达到与 a.__proto__ = {} 相同的效果。

    因此您看到的输出。

    希望对你有帮助。

    【讨论】:

    • 请不要使用__proto__,它已被弃用并会造成错误的印象。使用标准的Object.getPrototypeOf
    • @Bergi 当然,我不打算使用它。这只是为了解释 OP 所看到的行为背后的原因。我只是不想让他和其他细节混淆。他更容易在控制台窗口中看到 a1 对象的这个属性。
    • 除了它不是一个实际的属性,因此混淆了imo。
    • @Bergi 是的,你完全正确!但是,我希望解释正在发生的事情的更大原因不会因为这种用法而丢失。这绝不是用生产代码编写的,只是为了探索和实验。
    【解决方案4】:

    本质上,您正在为函数A 重新分配prototype 属性以指向一个新对象。这不会影响旧对象,因此不会影响之前的实例。

    这是正在发生的事情的图示。

    这段代码执行后:

    function A() {
      this.name = "cool";
    }
    
    A.prototype = {
      howCool: function() {
        return this.name + "er";
      }
    };
    
    var a1 = new A(),
      a2 = new A();
    

    情况如下:

    那么这段代码执行之后:

    A.prototype = {};
    
    var a3 = new A();
    

    A.prototype 指向一个新对象,但旧实例的 [[Prototype]] 属性仍指向旧对象。

    如果要真正删除方法,则必须编辑原始对象,而不是指向新对象。

    要真正从原型中删除 howCool() 方法,这样的事情会起作用:

    delete A.prototype.howCool
    

    这会给:

    现在任何未来的实例,例如a3,以及之前的实例,都仍将指向同一个对象,但该对象将没有howCool() 方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-16
      • 1970-01-01
      • 1970-01-01
      • 2010-11-29
      • 2013-12-04
      • 1970-01-01
      • 2015-08-04
      • 1970-01-01
      相关资源
      最近更新 更多