javascript_prototype

prototype

JavaScript中的对象有一个特殊的 [[Prototype]] 内置属性,其实就是对于其他对象的引用。几乎所有的对象在创建时 [[Prototype]] 属性都会被赋予一个非空的值。 但对象的[[Prototype]] 链接也可以为空,虽然很少见。

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

属性设置和屏蔽过程:

myObject.foo = "bar";
如果 myObject 对象中包含名为 foo 的普通数据访问属性,这条赋值语句只会修改已有的属性值。
如果foo 不是直接存在于 myObject 中,[[Prototype]] 链就会被遍历,类似 [[Get]] 操作。
如果原型链上找不到 foo,foo 就会被直接添加到 myObject 上。
然而,如果 foo 存在于原型链上层,赋值语句 myObject.foo = “bar” 的行为就会有些不同(而且可能很出人意料)。

1.如果在 [[Prototype]] 链上层存在名为 foo 的普通数据访问属性并且没 有被标记为只读(writable:false),那就会直接在 myObject 中添加一个名为 foo 的新 属性,它是屏蔽属性。
2.如果在 [[Prototype]] 链上层存在 foo,但是它被标记为只读(writable:false),那么 无法修改已有属性或者在 myObject 上创建屏蔽属性。如果运行在严格模式下,代码会 抛出一个错误。否则,这条赋值语句会被忽略。总之,不会发生屏蔽。
3.如果在 [[Prototype]] 链上层存在 foo 并且它是一个 setter ,那就一定会 调用这个 setter 。foo 不会被添加到(或者说屏蔽于)my Object,也不会重新定义 foo 这 个 setter 。

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

如果属性名 foo 既出现在 my Object 中也出现在 my Object 的 [[Prototype]] 链上层,那么就会发生屏蔽。(根据就近原则)

关联与复制

【在javascript中创建的对象通过prototype实现了关联,重点是关联;而在其他的“类”语言中,实现的是复制,重点在于复制。 】
new Foo() 只是间接完成了我们的目标:一个关联到其他对象的新对象。 更直接的方式是Object.create()。

Foo.prototype 默认(在foo声明时!)有一个公有并且不可枚举的属性 .constructor,这个属性引用的是对象关联的函数。
通过“构造函数”调用 new Foo() 创建的对象也有一个 .constructor 属性,指向 “创建这个对象的函数”。【注意:并非实例有这个属性,而是进行了原型链检索】
由于javascript中没有“类”的概念,也就没有构造函数的说法,只有构造函数调用;使用new就会劫持函数并用构造对象的形式调用它。
注意:一些随意的对象属性引用,比如 a1.constructor,实际上是不被信任的,它们不一 定会指向默认的函数引用。
要创建一个合适的关联对象,我们必须使用 Object.create(..) 而不是使用具有副作用的 Foo(..)。这样做唯一的缺点就是需要创建一个新对象然后把旧对象抛弃掉,不能直接修改已有的默认对象。
ES6 添加了辅助函数 Object.setPrototype Of(..),可以用标准并且可靠的方法来修改关联。
instanceof 操作符只能处理对象(a)和函数(带 .prototype 引用的 Foo)之间的关系。如果你想判断两个对象(比如 a 和 b)之间是否通过 [[Prototype]] 链关联,只用 instanceof 无法实现。
第二种判断 [[Prototype]] 反射的方法更加简洁:
Foo.prototype.is Prototype Of( a ); // true

原型机制:

就是指对象中的一个内部链接引用另一个对象。
在第一个对象上没有找到需要的属性或者方法引用;javascript引擎就会继续在原型关联的对象上进行查找;以此类推;直到找到或者检索到Object;这一系列对象的链接称为原型链

Object.create(null)会创建一个拥有空(或者说null)[[Prototype]] 链接的对象,这个对象无法进行委托。由于这个对象没有原型链,所以 instanceof 操作符(之前解释过)无法进行判断,因此总是会返回 false。
这些特殊的空 [[Prototype]] 对象完全不会受到原型链的干扰,因此非常适合用来存储数据。

委托行为意味着某些对象在找不到属性或者方法引用时会把这个请求委托给另一个对象。

star Huang wechat
my wechat public welcomes to your attention