javascript|[[Prototype]] ——原型链两万字全面解析「建议收藏」( 二 )


myObj.c =12

for(var k in myObj){
  console.log('found:' + k)


// 'found:c'
// 'found:a'
// 'found:b'

('a' in myObj); // true

console.log(myObj.d) // undefined


这里的代码是分别表现了原型绑定 , in 操作符 , 查找不到的会最后输出 undefined 。
因此 , 当你通过各种语法进行属性查找时都会查找 [[Prototype

链 , 直到找到属性或者查找完整条原型条 。
Object.prototype但是到哪里事是 [[Prototype

的「尽头」呢?
所有普通的 [[Prototype

链最终都会指向内置的 Object.prototype 。 由于所有的「普通」对象都「源于」这个 Object.prototype 对象 , 所以它包含了 JavaScript 中许多通用的功能 。

所有的原型链的最外层都是 Object.prototye , 这是 JavaScript 对象的基本功能 。
有些功能你应该已经很熟悉了 , 比如说 toString 和 valueOf , 以及之前介绍的 hasOwnPrototype 。 稍后我们还会介绍 isPrototypeOf , 这个你可能不太熟悉 。
属性设置和屏蔽给一个对象设置属性并不仅仅是添加一个新属性或者修改已有的属性值 。 现在我们完整地讲解一下这个过程 。
myObj.foo = 'bar'
如果 myObj 对象中包含名为 foo 的普通数据访问属性 , 这条赋值语句只会修改已有属性值 。
如果 foo 不直接存在于原型链的上层 , 赋值语句 myObj.foo = 'bar' 行为就会有些不同 , 而且可能出乎意料 。
如果属性名 foo 既出现走 myObj 中也出现在 myObj 的 [[Prototype

链上层 , 那么就会发生屏蔽 。 myObj 中包含的 foo 属性会屏蔽原型链上层所有的 foo 属性 , 因为 myObj.foo 总会选择原型链中最底层的 foo 属性 。

原型链有就近原则 , 当在附近找到时 , 就不会继续往上寻找 。
这里的屏蔽一词的意思是指:不再继续查找 。
屏蔽比我们想象的要复杂 , 下面我们分析一波如果 foo 不直接存在于 myObj 中而是存在原型链上层时 myObj.foo = 'bar' 的三种情况 。
1.如果在 [[Prototype

链上层存在名为 foo 的普通数据访问属性 , 并且没有标记为只读 , 那就会直接在 myObj 中添加一个名为 foo 的新属性 , 它是屏蔽属性 。 2.如果在 [[Prototype

上层存在 foo , 但是它被标记为只读 , 那么无法修改已有属性 , 或者在 myObj 上创建屏蔽属性 。 如果运行在严格模式下 , 代码会抛出一个错误 。 否则 , 这条赋值语句会被忽略 。 总之 , 不会发生屏蔽 。 3.如果在 [[Prototype

链上存在 foo , 并且它是一个 setter , 那就一定会调用这个 setter , foo 不会被添加到 myObj , 也不会重新定义 foo 这个 setter 。
大多数开发者都认为如果想 [[Prototype

链上层已经存在的属性 [[Put

赋值 , 就一定会触发屏蔽 , 但是如你所见 , 三种情况中只有第一种是这样的 。

屏蔽属性是指存在于属性存在于对象本身 , 而不存在于整个原型链上 。

在我看来 , 这里的三种情况 , 只有第一中是正常情况 , 第二种碰上的可能微乎其微 , 至于第三种 , 更是违反常规操作 , 开发这些年 , 从未见过不使用默认的 [[Get

和 [[Put

的 , 它只是为了表示知识的完整性 , 而深究于学院派的理论中 。
至于作者为什么要在这么讲这些看似没用 , 其实也没用的知识 , 可能只是基于学院派的理论学习 。 但在恰恰就是目前互联网所需要的 , 作为一个日渐成熟的产业 , 这些讨论会被提出来 , 也是很正常的事情 。