这是我参与8月更文挑战的第三十天,活动详情查看:8月更文挑战
属性设置与屏蔽
在JavaScript中,给一个对象设置属性不仅仅是添加一个新属性或者修改已有的属性值。 接下来我们完成的剖析一下这个过程:
myObject.foo = "bar";
如果myObject对象中包含名为foo的普通数据访问属性,那么这条赋值语句只会修改已有的属性值。
如果foo不是直接存在于myObject中,[prototype]链就会被遍历,类似于[Get]操作。如果在整个原型链上都找不到foo,那么就会在myObject上直接添加foo。
如果foo即出现在myObject中也出现在myObject的原型链上层,那么就会发生屏蔽。myObject中包含的foo属性会屏蔽原型链上层的所有foo属性,因为myObject.foo总会选择原型链中最底层的foo属性。
但是屏蔽并没有想象的那么简单。下面分析下,如果foo不直接存在于myObject中而是存在于原型链上层时的三种情况:
- 如果在[prototype]链上层存在名为foo的普通数据访问属性并且该属性没有被标记为只读(writable:ture),那就会直接在myObject中添加一个名为foo的新属性,它是屏蔽属性。
- 如果在[prototype]链上层存在foo,但是该属性被标记为只读(writable:false),那么无法修改已有属性或者在myObject上创建屏蔽属性。如果运行在严格模式下,代码会抛出错误。否则,会被忽略,总之就是不会发生屏蔽。
- 如果在[prototype]链上层存在foo并且它一个setter,那就一定会调用这个setter。foo不会被添加到(或者说屏蔽于)myObject,也不会重新定义foo这个setter。
只读属性会阻止[prototype]链下层隐式创建(屏蔽)同名属性。这样做主要是为了模拟类属性的继承。我们可以把原型链上层的foo看作父类中的属性,它会被myObject继承(复制),这样一来myObject中的foo属性也是只读,所以无法创建。但是我们要清楚,实际上并不会发生类似的继承复制。这是令人迷惑的,myObject对象竟然会因为其他对象中有一个只读foo就不能包含foo属性,更令人迷惑的是,这个限制只存在于=赋值中,使用Object.defineProperty()并不会收到影响。
有些情况下也隐式的产生屏蔽:
var anotherObj={
a : 2
};
//创建一个关联到anotherObj的对象
var myObj = Object.create(anotherObj);
console.log(anotherObj.a); //2
console.log(myObj.a); //2
anotherObj.hasOwnProperty("a") //true
myObj.hasOwnProperty("a") //false
myObj.a++; //隐式屏蔽!
console.log(anotherObj.a); //2
console.log(myObj.a); //3
myObj.hasOwnProperty("a") //true
尽管myObj.a++看起来应该查找并增加anotherObj.a属性,但是++操作相当于
myObj.a = myObj.a + 1。因此++操作首先会通过[prototype]查找属性a并从anotherObject.a获取当前属性值2,然后给这个值加1,接着用[Put]将值3赋给myObject中新建的屏蔽属性a。