javascript的属性设置与屏蔽

385 阅读2分钟

注: 给一个对象设置属性并不仅仅是添加一个新属性或者修改已有属性值。欲知详情,往下看...

myObject.foo = "bar";

如果myObject对象中包含名为foo的普通数据访问属性,这条赋值语句只会修改已有的属性值。

如果foo不是直接存在于myObject中,[[Prototype]]链就会被遍历,类似 [[Get]]操作。如果原型链上找不到foo,foo就会被直接添加到myObject上。

但,如果foo存在于原型链上层,赋值语句myObject.foo = "bar"的行为就会有些不同(而且可能出乎你的意料)。接下来继续往下看

如果属性名foo既出现在myObject中也出现在myObject的[[Prototype]]链上层,那么就会发生屏蔽。myObject中包含的foo属性会屏蔽原型链上层的所有foo属性,因为myObject.foo总是会选择原型链中最底层的foo属性。

屏蔽比我们想象中更加复杂

下面我们分析一下如果foo不直接存在于myObject中而是存在于原型链上层时myObject.foo = "bar"会出现的三种情况。

  1. 如果在[[Prototype]]链上层存在名为foo的普通数据访问属性,并且没有被标记为只读(writable:false),那就会直接在myObject中添加一个名为foo的新属性,它就是屏蔽属性
  2. 如果在[[Prototype]]链上层存在foo,但是它被标记为只读(writable:false),那么无法修改已有属性或者在myObject上创建屏蔽属性。如果运行在严格模式下,代码会抛出一个错误。否则,这条赋值语句会被忽略。总之不会发生屏蔽。
  3. 如果在[[Prototype]]链上层存在foo并且它是一个setter,那就一定会调用这个setter。foo不会被添加到(或者说屏蔽于)myObject,也不会重新定义foo这个setter。

大多数开发者都认为如果向[[Prototype]]链上层已经存在的属性([[Put]])赋值,就一定会触发屏蔽,但如你所见,三种情况中只有一种(第一种)是这样的。

如果你希望在第二种和第三种情况下也屏蔽foo,那就不能使用=操作符来赋值,而是使用Object.defineProperty(..)来向myObject添加foo。


内容源于【你不知道的javascript】一书。