【问题标题】:How do Javascript object instances retain methods after parent class is redefined?重定义父类后,Javascript对象实例如何保留方法?
【发布时间】:2019-07-10 19:23:00
【问题描述】:

我正在尝试理解 Javascript 原型继承,但无法理解对象实例与其各自类的原型属性之间的关系,该原型属性存储继承的方法。

我了解在类的原型属性中定义方法允许实例访问/继承这些方法,这样就不会重新定义方法。大概这是通过允许实例指向内存中的方法的指针(或类似的东西)来实现的。我也理解为什么向类的原型属性添加方法允许先前创建的实例访问这些“新”方法,因为实例只是访问它们的原型属性(包括它们派生的类),而原型属性又访问类的原型属性。

理解的是以下场景:

  1. 我用方法定义了一个平凡的对象:

    function Person(name) {
      this.name = name;
    }
    Person.prototype.greeting = function() {
      return "Hello, " + this.name;
    }
    
  2. 我创建了一个实例并且可以访问数据:

    var p = new Person("Ryan");
    p.name //"Ryan"
    p.greeting() //"Hello, Ryan"
    
  3. 重新定义没有问候:

    Person = function (name) {
      this.name = name;
    }
    
  4. 我仍然可以使用上一个实例的问候语:

    p.greeting() //"Hello, Ryan" but expected error

如果 Person 被重新定义,它会覆盖内存中先前的 Person 构造函数,如果不再有任何与 Person 关联的此类方法,实例“p”如何仍然可以访问 greeting 方法?实例实际上是在它们自己的原型字段中复制其继承类方法的位置吗?如果是这样的话,如果实例直接将这些方法的位置存储在自己的原型字段中,为什么总是谈论处理器必须“走上”才能找到继承方法的“原型链”?

非常感谢您的帮助。如果我错过了一些简单的事情,我深表歉意。

【问题讨论】:

  • 您只是在创建一个新类(并覆盖变量Person),但您不会更改p 是其实例的类或其继承的原型。
  • @Bergi 这作为对正在发生的事情的描述是有道理的,但我不明白为什么相同的内存区域没有被覆盖 - 我想这是对象和此类数据特有的功能因为 JS 开发人员预计会重写函数,所以类型?因为如果我将一个整数分配给一个变量并重新分配该变量,那么内存中的相同位置将被使用,不是吗?还是上面的函数的原始位置没有被重写,因为仍然有一个指向它的活动指针,所以新请求的内存超出了内存的限制?
  • JS 中的对象没有固定的位置,不能覆盖。该变量仅包含对内存中某处的对象(或函数对象,在这种情况下)的引用。创建新函数会在其他地方分配新内存,分配给变量只会更改存储在该变量中的引用。其他东西(比如你的 p 实例)仍然引用旧对象。 JavaScript 是一种面向对象的垃圾回收语言。

标签: javascript object javascript-objects


【解决方案1】:

您正在混淆指向对象和对象本身的名称。使用时:

Person = function (name) {
  this.name = name;
}

没有重新定义名称Person 指向的对象。您只是将名称 Person 指向一个新事物——一个新功能。旧的Person.protoype 仍在内存中,实例p 持有对它的引用,p.constructor 仍然指向原始的Person 函数。

您可以通过获取 Person.prototype 的引用并比较重新分配变量 Person 之前和之后来确认这一点

function Person(name) {
  this.name = name;
}
Person.prototype.greeting = function() {
  return "Hello, " + this.name;
}
var p = new Person("Ryan");

// p's prototype is Person.prototype
console.log(Object.getPrototypeOf(p) === Person.prototype) // true

// grab a reference of the protoype for later
let Pprotot = Person.prototype

// point variable Person at something else
Person = function (name) {
  this.name = name;
}

// p's prototype is STILL the old Person.prototype
console.log(Object.getPrototypeOf(p) === Pprotot)    // still true

// p also still holds a reference to the old function (through the prototype)
console.log(p.constructor)

// but it's not the same thing the variable Person now points to
console.log(p.constructor === Person)

【讨论】:

  • 任何给定实例中的 proto 字段然后存储一个引用/指针,指向它从内存中的该位置继承的最直接父对象的原型的另一个 可以访问 proto 字段以向上移动链,直到到达 Object,对吗?那么,我唯一的另一个问题是,为什么我会看到 Object.getPrototypeOf(p) 返回的方法声明?这将返回: Person {greeting: function} 但我希望继承的方法是 within Person 的原型字段,而不是实例的 proto 字段(Object.getPrototypeOf([]) 没有返回数组方法的列表)。
  • @soporific312 不,存储它的不是.__proto__ 属性,而是应该使用Object.getPrototypeOf 访问的内部字段。 __proto__ getter 已弃用。但是是的,这个东西定义了继承层次结构。
  • @soporific312 console.loging 原型显示了 Person.prototype 对象 - 您可以直接记录它以查看它是否相同。请注意,记录 Array.prototype 不会显示任何方法,因为它们不可枚举,但它们仍然存在。
  • @soporific312 您可以使用console.log(Object.getOwnPropertyDescriptors(Object.getPrototypeOf([]))) 更好地了解数组原型上的内容。这将列出方法。
  • @Bergi 这真的很有帮助——谢谢。我看到 proto 已被弃用 - 那么内部字段是否只能由 Object.getPrototypeOf getter 访问?我不明白您所说的“因为它们不可枚举”是什么意思 - 问候功能也不可枚举?
【解决方案2】:

我刚刚做了一个小测试,变量 p 似乎还在记住之前的对象。

Person = function(name) {
  this.name = name;
}
const s = new Person('hello');
s.name; //hello
s.greeting(); //s.greeting is not a function

【讨论】:

  • 是的,它确实记得之前的对象 - 正如 Mark Meyer 提到的,之前创建的对象仍然指向内存中定义原始对象的位置。
猜你喜欢
  • 2016-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多