[[prototype]]

js中的对象都有一个内置的 [[Prototype]] 属性,其实就是对于其他对象的引用。

在访问对象属性,触发 [[Get]] 操作的时候,先在对象本身查找是否有这个属性,没有的话就顺着 [[Prototype]] 原型链查找下去,访问到顶端还没有的话返回undefined。

for…in遍历对象(可枚举的对象)的原理也是这样的

1
2
3
4
5
6
7
8
9
10
11
var obj = {
a:2
};
//把myObj对象的[[prototype]]关联到obj上
var myObj = Object.create(obj);
for(var i in myObj){
console.log(i);
}
//同样,in操作符也是同样道理,会遍历原型链
console.log(("a" in myObj)); //true

所有 [[Prototype]] 链最终指向Object.prototype

属性的设置和屏蔽

1
myObj.foo = 'bar'

这个语句在不同场景下有不同的操作:

  • myObj对象中有foo这个属性

    此时的操作就是在修改已有的属性

  • myObj对象中没有foo这个属性,并且原型链上也没有这个属性

    此时的操作,先遍历了myObj的原型链,类似于[[Get]]操作

    没有找到对应的属性,于是foo属性会被添加到myObj上

  • foo属性既存在在myObj对象中,又出现在它的原型链上

    此时的操作会发生屏蔽,对象中的foo属性会屏蔽原型链上所有foo属性

    因为在获取的时候,总是会获取最底层的属性

  • myObj对象中没有foo这个属性,但是原型链上有这个属性

    • 原型链上的foo属性是可访问可修改的

      此时操作会发生屏蔽,直接在myObj对象上添加foo属性

    • 原型链上的foo属性被标记为只读(writable:false)

      此时无法修改原型链上已有属性,也无法在对象上创建foo(不会发生屏蔽)

      在严格模式下会报错,非严格模式下赋值语句将被忽略

    • 原型链上的foo是个setter

      此时一定会调用这个setter,不会发生屏蔽,也不会重新定义foo这个setter

      1
      2
      *注:setter会覆盖单个属性默认的[[PUT]]操作
      如果药第二第三种情况也发生屏蔽,需要使用Object.defineProperty(...),而不是=赋值操作