构造函数原型的更改会影响实例化的对象吗?

Will change of constructor's prototype influence the instantiated object?

提问人:McBacon 提问时间:10/30/2023 最后编辑:isherwoodMcBacon 更新时间:10/31/2023 访问量:52

问:

如果我有一个带有定义原型的函数(构造函数),假设:

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

Student.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}`);
}

我使用该函数实例化和对象(据我所知,它应该在创建时将新创建的对象的属性设置为上述原型对象,并将属性设置为函数)。_prototypeconstructorStudent

const s1 = new Student('John');
s1.sayHello();

我确定调用会起作用,但是,如果我像这样更改原型怎么办?sayHello()Student

Student.prototype.sayGoodbye = function() {
  console.log(`Goodbye, my name is ${this.name}`);
}

应该可以打电话吗?s1sayGoodbye

我已经读到不应该改变,所以在这个逻辑中它应该抛出一个错误,但是,在 Chrome 的控制台面板和 node.js 项目中尝试它,它工作并且 s1 可以调用 .有人可以帮我理解这一点吗?_prototypesayGoodbye

JavaScript 函数 对象 原型

评论

3赞 deceze 10/30/2023
实例化对象时不会复制原型。从字面上看,它指向一个原型对象。您添加到该对象的任何内容在指向它的任何内容上都是可见的。
0赞 McBacon 10/30/2023
哦,感谢您的输入:)。这更有意义。对原型的引用将在对象的整个生命周期内持续存在,并在由同一函数构造的所有对象之间共享。只有当我们已经有几个具有相同原型的对象并且原型将更改为新对象时,下一个对象才会有新的引用。

答:

0赞 Amadan 10/30/2023 #1

我不确定是什么意思。你的意思是 ?或?这两者是不同的东西(并且被弃用,取而代之的是 和 ,以及类似的方法)。_prototype.__proto__.prototype.__proto__Object.getPrototypeOfObject.setPrototypeOfReflect

“改变原型”有几种不同的含义。

  1. 将 ,如,替换为 。除了对可读性的可能影响之外,还有性能因素,MDN 的警告中总结了这一点:[[Prototype]]Object.setPrototypeOf

    警告:根据现代 JavaScript 引擎优化属性访问的本质,更改对象目前在每个浏览器和 JavaScript 引擎中都是非常缓慢的操作。此外,更改继承的影响是微妙而深远的,不限于在语句中花费的时间,而是可以扩展到任何可以访问任何已被更改的对象的代码。您可以在 JavaScript 引擎基础知识:优化原型中阅读更多内容。[[Prototype]]Object.setPrototypeOf(...)[[Prototype]]

    由于此功能是语言的一部分,因此引擎开发人员仍然需要高性能地(理想情况下)实现该功能。在引擎开发人员解决此问题之前,如果您担心性能,则应避免设置对象。相反,请使用 创建一个具有所需 的新对象。[[Prototype]][[Prototype]]Object.create()

  1. 通过替换或添加属性来修改内置对象或库对象。不鼓励这样做,主要是因为它不是面向未来的。例如,是一个非常有用的函数,它在 2012 年并不存在。如果有人将它添加到数组原型中,那么它会很有用,但是当它在几年后实现时,任何自己定义的人都会用效率较低的代码替换效率更高的代码。[[Prototype]]Array.prototype.findArray.prototype.find

    您可以防范它,并且仅在尚未定义函数时才替换该函数,但可能会出现不兼容问题。当 now-standard 找不到匹配的元素时,它将返回。如果你的 polyfill 正在返回,但你不理会内置的,以防它存在,并且你的代码在 和 上的行为不同,你就有问题了。.findundefinednull.findnullundefined

  2. 通过定义或替换原型上的属性来修改您自己的对象。这似乎是您在问题中使用的含义。耸肩。这是你的代码。[[Prototype]]

    是的,对原型属性的任何更改都将反映在原型对象中,除非它们被原型对象上的属性所掩盖。正如 deceze 在他们的评论中解释的那样,原型属性在创建时不会复制到原型对象上。相反,在查找时,在原型对象上检查该属性,然后在原型上检查,然后在原型的原型上检查该属性,直到找到它(或者,如果在根对象到达链的末尾之前找不到此类属性,则返回)。因此,如果将属性添加到原型中,则添加属性的时间(无论是在创建原型对象之前还是之后)都没有区别,只要是在访问该属性之前。undefined

0赞 werad saoud 10/31/2023 #2

作为您问题的答案“s1 应该能够呼叫 sayGoodbye 吗? 是的,它应该。 要弄清楚为什么。你必须了解 [[prototype]] 链。任何构造函数,如您的函数:

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

是具有 .prototype 属性的对象。该构造函数的任何实例都将具有一个属性,该属性将指向该属性,而该属性又指向 Object.prototype([[prototype]] 链的末尾)。 现在,当你向该链中的任何原型节点添加一个属性时 - 假设 - 它将被实例看到,因为当你像这样调用它时: ,JS 引擎将在 Student 对象中查找它,如果它没有找到它,那么引擎将在引用的对象中查找它, 在我们的例子中,如果它也没有在那里找到它,它将继续在 [[prototype]] 链中徘徊,直到它找到它或到达链的末端,即 .var student = new Student("name");__proto__Student.prototype__proto__sayGoodbyestudentstudent.sayGoodbye()student.__proto__Student.prototypeObject.prototype