我在 Chrome 中做了一些测试。 很简单,我改变继承的属性值。 我假设该集合将发生在原型(prototype)对象上,但原型(prototype)值不会改变。
实际上,在子对象上创建了一个新属性。 这是预期的吗?
> function Shape() {};
> var base = new function() { this.mValue = 10; };
> Shape.prototype = base;
> var s = new Shape();
> s.mValue = 20;
> base;
< Object {mValue: 10}
最佳答案
Set the property value inherited from prototype, why the prototype value does not change?
因为当您分配给 s.mValue
时,它会在 s
上创建一个新属性,该属性会遮蔽原型(prototype)上的属性。
属性访问的工作原理如下:
如果您获取属性的值,它首先查看对象本身以查看是否具有该值。如果是,则返回该对象的值。如果没有,它会查找对象的原型(prototype),如果原型(prototype)有,则返回原型(prototype)的值;如果没有,则转到原型(prototype)的原型(prototype),依此类推。
如果您设置属性的值,它会将其设置在对象本身上,而不是沿着原型(prototype)链向上。
显然这是简单的版本。 :-)
如果您是从基于类的语言接触到它,这可能会有点令人惊讶,但是一旦您理解了它,它就非常简单。
为了清楚起见,让我们添加一些 ASCII 艺术。在此代码之后:
function Shape() {};
var base = new function() { this.mValue = 10; };
Shape.prototype = base;
...我们的内存中有这样的内容:
+−−−−−−−−−−−−−−−−−−+ Shape−−>| function | +−−−−−−−−−−−−−−−−−−+ | [[Prototype]] |−−−−−> Function.prototype | prototype |−+ +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ +−−−>| object | | +−−−−−−−−−−−−−−−+ base−−−−−−−−−−−−−−−−−−−−−−−−−+ | [[Prototype]] |−−−> Object.prototype | mValue: 10 | +−−−−−−−−−−−−−−−+
(If prototype
vs. [[Prototype]]
is unfamiliar to you, see the explanation at ¹ at the end of the answer.)
Then we do:
var s = new Shape();
并得到:
+−−−−−−−−−−−−−−−−−−+ Shape−−>| function | +−−−−−−−−−−−−−−−−−−+ | [[Prototype]] |−−−−−> Function.prototype | prototype |−+ +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ +−+−>| object | | | +−−−−−−−−−−−−−−−+ base−−−−−−−−−−−−−−−−−−−−−−−−−+ | | [[Prototype]] |−−−> Object.prototype | | mValue: 10 | +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ s−−−−−−>| object | | +−−−−−−−−−−−−−−−−−−+ | | [[Prototype]] |−−−+ +−−−−−−−−−−−−−−−−−−+
At this point, if we did console.log(s.mValue)
, the JavaScript engine would look at s
and, not finding a property with the name mValue
, it would follow the __proto__
link to get the prototype, and look there. Finding it there, it gets the value from there.
Then we do:
s.mValue = 20;
并得到:
+−−−−−−−−−−−−−−−−−−+ Shape−−>| function | +−−−−−−−−−−−−−−−−−−+ | [[Prototype]] |−−−−−> Function.prototype | prototype |−+ +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ +−+−>| object | | | +−−−−−−−−−−−−−−−+ base−−−−−−−−−−−−−−−−−−−−−−−−−+ | | [[Prototype]] |−−−> Object.prototype | | mValue: 10 | +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ s−−−−−−>| object | | +−−−−−−−−−−−−−−−−−−+ | | [[Prototype]] |−−−+ | mValue: 20 | +−−−−−−−−−−−−−−−−−−+
At this point, if we did console.log(s.mValue)
, the JavaScript engine would look at s
, find the property, and get its value from s
.
Side note: var base = new function() { this.mValue = 10; };
is a really long way to write var base = { mValue: 10 }
. ;-) And it very very very slightly slows down access to the properties inherited from Object.prototype
, as it inserts an extra unnecessary prototype in the chain. Also, you don't need to create a new object for Shape
to assign to instances; it already has one.
So that code can be simplified to:
function Shape() {} // No need for ; after function declarations, they're not statements
Shape.prototype.mValue = 10;
var s = new Shape();
s.mValue = 20;
Shape.prototype;
(从 2023 年更新答案开始,我们可能不会使用 var
。)
只是FWIW。
1 关于原型(prototype)
和[[Prototype]]
:
prototype
是某些函数具有的属性,它指向要分配为通过该函数通过new
创建的对象的原型(prototype)的对象。示例:Date.prototype
指向用作通过new Date
创建的日期实例原型(prototype)的对象。[[Prototype]]
是对象的内部字段,指向对象的原型(prototype)。示例:如果您执行了const d = new Date()
,则d'
的[[Prototype]]
字段指向Date。原型(prototype)
。[[Prototype]]
字段无法在代码中直接访问。您可以使用Object.getPrototypeOf(d)
来访问它,或者(不推荐)使用Object.prototype
中定义的名为__proto__
的访问器属性来访问它>。 (但是不要在新代码中使用 __proto__ 。它只是为了向后兼容为 JavaScript 引擎编写的代码,该引擎在标准化之前添加了该功能,因此没有必要,请注意,不继承的对象来自Object.prototype
没有它。)
关于javascript - 设置继承自prototype的属性值,为什么prototype值没有改变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29790292/