专栏名称: 钱塘风华
一湾钱塘,一片风华,一个陪你走心的文艺暖男,...
今天看啥  ›  专栏  ›  钱塘风华

Day42:[[Prototype]]

钱塘风华  · 简书  ·  · 2019-05-05 23:41

文章预览

【书名】:你不知道的JavaScript(上卷)

【作者】:Kyle Simpson

【本书总页码】:213

【已读页码】:161

JavaScript 中的对象有一个特殊的 [[Prototype]] 内置属性,其实就是对于其他对象的引用。几乎所有的对象在创建时 [[Prototype]] 属性都会被赋予一个非空的值。

注意:对象的 [[Prototype]] 链接可以为空。

Object.create(..)会创建一个对象并把这个对象的 [[Prototype]] 关联到指定的对象。

现在 myObject 对象的 [[Prototype]] 关联到了 anotherObject。显然 myObject.a 并不存在,但是尽管如此,属性访问仍然成功地(在 anotherObject 中)找到了值 2。

但是,如果 anotherObject 中也找不到 a 并且 [[Prototype]] 链不为空的话,就会继续查找下去。

这个过程会持续到找到匹配的属性名或者查找完整条 [[Prototype]]链。如果是后者的话,[[Get]] 操作的返回值是 undefined。

使用 for..in 遍历对象时原理和查找 [[Prototype]] 链类似,任何可以通过原型链访问到(并且是 enumerable)的属性都会被枚举。使用 in 操作符来检查属性在对象中是否存在时,同样会查找对象的整条原型链(无论属性是否可枚举):

因此,通过各种语法进行属性查找时都会查找 [[Prototype]] 链,直到找到属性或者查找完整条原型链。

1. Object.prototype

所有普通的 [[Prototype]] 链最终都会指向内置的 Object.prototype。由于所有的“普通”(内置,不是特定主机的扩展)对象都“源于”(或者说把 [[Prototype]] 链的顶端设置为)这个 Object.prototype 对象,所以它包含 JavaScript 中许多通用的功能。

2. 属性设置和屏蔽

给一个对象设置属性并不仅仅是添加一个新属性或者修改已有的属性值。

myObject.foo = "bar";

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

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

如果属性名 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。

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

第二种情况可能是最令人意外的,只读属性会阻止 [[Prototype]] 链下层隐式创建(屏蔽)同名属性。这样做主要是为了模拟类属性的继承。可以把原型链上层的 foo 看作是父类中的属性,它会被 myObject 继承(复制),这样一来 myObject 中的 foo 属性也是只读,所以无法创建。但是一定要注意,实际上并不会发生类似的继承复制。

如果需要对屏蔽方法进行委托的话就不得不使用丑陋的显式伪多态。

有些情况下会隐式产生屏蔽,一定要当心:

尽管 myObject.a++ 看起来应该(通过委托)查找并增加anotherObject.a 属性,但是别忘了 ++ 操作相当于 myObject.a = myObject.a + 1。因此 ++ 操作首先会通过 [[Prototype]]查找属性 a 并从 anotherObject.a 获取当前属性值 2,然后给这个值加 1,接着用[[Put]]将值 3 赋给 myObject 中新建的屏蔽属性 a。

修改委托属性时一定要小心。如果想让 anotherObject.a 的值增加,唯一的办法是anotherObject.a++。

………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览