JS中对象属性的读取与设置详解

属性的读取

在语言规范中,object.a在object上实际上是实现了[[Get]]操作。对象默认的[[Get]]操作首先在对象中查找是否有相同的属性名,如果有就返回这个属性。如果没有,就会继续访问对象的[[Prototype]]链。如果无论如何都没找到相同的属性,那么[[Get]]操作就会返回undefined。(相对的,如果你访问一个词法作用域中不存在的变量,不会像对象属性这样返回undefined,而是返回ReferenceError)

 

属性的设置

给属性赋值实现的是[[Put]]操作,情况稍微复杂点。

myObject.foo=”bar”;

  • 如果对象中已经存在这个属性,按如下步骤运行:
  1. 属性访问描述符是否存在setter,如果有的话就调用setter。
  2. 属性数据描述符中writable是否是false?若是,在非严格模式下静默失败,在严格模式下抛出TypeError异常。
  3. 如果都不是,将该值设置为属性的值。
  • 如果对象myObject中不存在这个属性foo,[[Prototype]]链就会被遍历,类似[[Get]]操作。如果原型链上找不到这个属性foo,这个属性foo就会直接被添加在对象myObject上。
  • 如果foo不存在于myObject中而是存在于原型链上层时,foo=”bar”会出现以下三种情况:
  1. 如果在[[Prototype]]链上层存在名为foo的普通数据访问属性,并且没有被标记为只读(writable:false),那么会在myObject中添加一个名为foo的新属性,它是屏蔽属性。
  2. 如果在[[Prototype]]链上层存在名为foo的普通数据访问属性,但是被标记为只读了(writable:false),那么无法修改现有属性或者在myObject上创建屏蔽属性。运行在严格模式下,会抛出一个错误。否则,这条语句会被忽略。总之,不发生屏蔽。这种做法主要是模拟类的继承,你可以吧原型链上层中的foo看做父类的属性,他会被myObject继承,这样foo也是只读,无法创建(A.2)。但是以上的限制只存在于等号=赋值中,Object.defineProperty(..)不受影响。
  3. 如果在[[Prototype]]链上层存在名为foo并且他是一个setter,那就一定调用这个setter。Foo不会被添加到(或者说屏蔽)myObject,也不会重新定义foo这个setter。