一.原型 [[Prototype]]
原型 [[Prototype]] 是 JavaScript 中对象的一个内置属性。它本质实际上是对其他对象的引用,基本上对象在创建的时候 [[Prototype]] 都会获得一个非空的值。
[[Prototype]] 有什么作用?例:
var obj1 = {
a:2
}
var obj2 = Object.create(obj1);
obj2; // {}
obj2.a; // 2
上面的例子中,obj2 对象里面没有 a,这时候就用到了 [[Prototype]] 链,将会在 [[Prototype]] 链上寻找属性 a。因为使用 Object.create 关联了 obj1 和 obj2,obj2 的 [[Prototype]] 指向 obj1,并在 obj1 中找到了 a并返回它。
- obj2.a 是对对象属性 a 的引用的操作,这个操作触发 [[Get]] , [[Get]] 操作第一步是检查这个对象里面有没有这个属性,有就使用它。如果没有,会在 [[Prototype]] 链(obj1)上寻找这个属性,如果在 obj1 中也没找到,会在 obj1 的 [[Prototype]] 链上寻找,一层一层寻找直到顶层。到顶层都没找到则返回 undefined。
如果使用 for … in 遍历对象,操作和查找 [[Prototype]] 链是类似的。任何可以通过原型链访问到的并且是可枚举的属性,同样会枚举。使用 in 操作符的话,无论是否可枚举都会被查找。请看下面的例子:
二.原型链
从上一章的例子中知道,原型链是一层一层的,但是它们的顶层是 Object.prototype。
所有普通的 [[Prototype]] 链最终都会指向 Object.prototype,因为这个原因,创建普通对象都可以引用到它里面的属性,所以里面放入了许多功能,如.toString() 和 .valueOf(),.hasOwnProperty(…)等。
了解了从原型链上查找的动作,如果某次操作对原型链上的属性进行“=”赋值,如:“ obj2.a = ‘b’ ”,会出现三种情况:
- 原型链上存在此属性并且是 setter,则会调用setter,setter不会被重新定义。
- 原型链上存在此属性且是普通的属性描述符,且属性不为只读,那么就会直接在 obj2 中添加属性 a,它是屏蔽属性。
- 原型链上存在此属性且属性描述为只读,则无法创建屏蔽属性或修改原来的属性。如果运行在严格模式下,则会报错。
屏蔽属性:如果执行 obj2.a = ‘b’ ,因为 obj2 里面没有 a 属性,原型链上 a 属性不为只读,那么创建一个 a 属性在 obj2 里面,以后可以直接在 obj2 里面找到 a 属性,屏蔽掉原型链上的属性。
可以看到只有第二种情况才会触发屏蔽,因此值得我们注意。