原型
[[ProtoType]]
JavaScript中对象有一个特殊的[[Prototype]]内置属性,就是对其他对象的引用。
for…in遍历对象时的原理和查找完整条[[Prototype]]链类似 ,任何可以通过原型链访问到的属性都会被枚举。使用in操作符检查属性在对象中是否存在时,同样也会查找整条原型链,无论属性是否可枚举。
Object.prototype
所有普通的[[Prototype]]链都会指向内置的Object.prototype。
属性设置和屏蔽
myObjet.foo = "newman";
foo不直接存在于myObject中而是在于原型链上层时的情况:
-
如果在 [[Prototype]] 链上层存在名为 foo 的普通数据访问属性(参见第 3 章)并且没有被标记为(writable:false),那就会直接在 myObject 中添加一个名为 foo 的新属性,它是屏蔽属性。
-
如果在 [[Prototype]] 链上层存在 foo,但是它被标记为只读(writable:false),那么无法修改已有属性或者在myObject 上创建屏蔽属性。如果运行在严格模式下,代码会抛出一个错误。否则,这条赋值语句会被忽略。总之,不会发生屏蔽。
-
如果在 [[Prototype]] 链上层存在 foo 并且它是一个 setter(参见第 3 章),那就一定会调用这个 setter。 foo 不会被添加到(或者说屏蔽于) myObject,也不会重新定义 foo 这个 setter。
隐式屏蔽
var protoObject = {
n:1
};
var myObj = Object.create(protoObject);
protoObject.a;//2
myObj.a;//2
protoObject.hasOwnProperty("a");//true
myObj.hasOwnProperty("a")//false
myObj.a++;//隐式屏蔽
protoObject.a;//2
myObj.a;//3
myObj.hasOwnProperty("a")//true
myObj.a++ 相当于 myObj.a = myObj.a + 1,该操作会查找属性a(通过[[Prototype]]),然后给其加1,再用[[Put]]将值赋值给myObj中新建的屏蔽属性a。
继承意味着复制操作,JavaScript并不会复制对象属性。相反,JavaScript会在两个对象之间创建一个关联,这样一个对象就可以通过委托访问另一个对象的属性和函数。
“构造函数”
function Foo(){
//...
}
Foo.prototype.constructor === Foo;//true
var a = new Foo();
a.constructor === Foo;//true
.constructor引用被委托给了Foo.prototype,而Foo.prototype.constructor默认指向Foo。
a.constructor只是通过默认的[[Prototype]]委托指向Foo,这和"构造"毫无关系。
Foo.constructor的.constructor属性只是Foo函数在声明时的默认属性。若创建了一个新对象并替换了函数默认的.prototype对象引用,新对象并不会自动获得.constructor属性。
函数不是构造函数,但是当且仅当使用new时,函数会变成“构造函数调用”。
两种把Bar.prototype关联到Foo.prototype的方法:
// ES6 之前需要抛弃默认的 Bar.prototype
Bar.ptototype = Object.create( Foo.prototype );
// ES6 开始可以直接修改现有的 Bar.prototype
Object.setPrototypeOf( Bar.prototype, Foo.prototype );
对象关联
[[Prototype]]机制就是存在域对象中的一个内部链,他会引用其他对象。如果在对象上没有找到需要的属性或者方法引用,引擎就
会继续在 [[Prototype]] 关联的对象上进行查找。同理,如果在后者中也没有找到需要的引用就会继续查找它的 [[Prototype]],以此类推。这一系列对象的链接被称为“原型链”。
Object.create(…)
var foo = {
something: function() {
console.log( "Tell me something good..." );
}
};
var bar = Object.create( foo );
bar.something(); // Tell me something good...
Object.create(…)会创建一个新对象并把它关联到我们指定的对象。
小结
-
访问对象中并不存在的属性,[[Get]]操作会查找对象内部[[Prototype]]关联的对象,此关联关系实际上定义了一条“原型链”,查找属性时会对它进行遍历。
-
所有普通对象都有内置的Object.prototype,指向原型链顶端,如果在原型链中找不到指定的属性就会停止。
-
关联两个对象常用方法是使用new关键词进行函数调用。
-
JavaScript不会进行复制(“类继承”),对象之间是通过内部的[[prototype]]链的关联。
-
对象之间的关系不是复制而是委托。