在上一节屡次提到[[Prototype]]
链,但没解释它究竟是什么,那么这一节咱们来详细介绍一下。学习
JavaScript
中的每一个对象都拥有一个原型对象。其实就是对于其余对象的引用。几乎全部的对象在建立时[[Prototype]]
属性都会被赋予一个非空的值。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链。(mdn有详细的说明)ui
为了学习体验,咱们打开控制台而后输入如下代码:this
var object = {
name:"tom"
};
object.__proto__
复制代码
能够看到控制台打印了一堆属性和方法,这个咱们后续说明。spa
思考下面的代码:prototype
var object = {
name:"tom"
}
var newObject = Object.create(object);
newObject.name; // "tom"
复制代码
这里[[Prototype]]
引用就发挥了做用,当咱们试图访问newObject
对象的name
属性时会触发[[Get]]
操做,[[Get]]
操做的第一步是检查对象自己是否有这个属性,若是有的话就使用它。但若是name
不在newObject
中,就要使用对象的[[Prototype]]
链了。设计
如今newObject
对象的[[Prototype]]
关联到了object
。显然newObject.name
并不存在,可是它在 object
中找到了name
属性。可是若是object
中也找不到name
而且[[Prototype]]
链不为空,就会继续查找下去。3d
这个过程会持续到找到匹配的属性名或者查找完整条[[Prototype]]
链。若是是后者的话,[[Get]]
操做的返回值是undefined
。(for..in
遍历对象时原理和查找[[Prototype]]
链相似)code
从图中,能够看出来[[Prototype]]
链最终都会指向内置的Object.prototype
,这上面有许多咱们熟悉的功能,好比toString()
和valueOf()
,hasOwnProperty(..)
,.isPrototypeOf(..)
等等。cdn
给一个对象设置属性并不单单是添加一个新属性或者修改已有的属性值。对象
如今咱们来学习一下这个过程:
var object = {};
object.name = "tom";
复制代码
object
对象中包含名为name
的数据访问属性,那么这条赋值语句只会修改已有的属性值。name
不在object
中,[[Prototype]]
链就会被遍历,相似[[Get]]
操做。若是原型链上找不到name
,name
就会被直接添加到object
上。name
既出如今obejct
中也出如今obejct
的[[Prototype]]
链上,那么就会发生屏蔽。obejct
中包含的name
属性会屏蔽原型链上的全部name
属性,所以object.name
老是会选择原型链中最底层的name
属性。name
仅存在于原型链上层,那么赋值语句object.name = "tom"
的行为就会有些不一样。下面分析一下若是name
是存在于原型链上时object.name = "tom"
会出现的三种状况。
[[Prototype]]
链上存在名为name
的数据访问属性而且没有被标记为只读(writable:false
),那就会直接在obejct
中添加一个名为name
的新属性,它是屏蔽属性。var object = {};
var other = Object.create(object);
other.name = "tom"; //默认other的defineProperty的writable属性为true
console.log(object.name); //tom
复制代码
[[Prototype]]
链上存在name
,可是它被标记为只读(writable:false
),那么没法修改已有属性或者在obejct
上建立屏蔽属性。var object = {};
var other = Object.create(object);
other.name = "tom";
Object.defineProperty(other,"name",{
writable:false
});
console.log(object.name); //undefined
复制代码
[[Prototype]]
链上存在name
而且他是一个Setter
,那就必定会调用这个Setter
。name
不会被添加到object
上,也不会从新定义name
这个Setter
。var object = {};
var other = Object.create(object);
Object.defineProperty(other,"name",{
get:function(){
return this._name;
},
set:function(){
this._name = "tom";
}
});
console.log(object.name); //undefined
复制代码
var object = {
num: 6
};
var newObject = Object.create(object);
console.log(newObject.num); //6
console.log(object.num); //6
console.log(newObject.hasOwnProperty("num")); //false
console.log(object.hasOwnProperty("num")); //true
newObject.num++;
console.log(newObject.num); //7
console.log(object.num); //6
复制代码
这里其实也比较好说明,执行++
操做至关于newObject.num=newObject.num+1
。所以++
运算首先会经过[[Get]]
操做在[[Prototype]]
查找属性num
并从object.name
获取当前属性值,而后给这个值加1,接着用[[Set]]
将值7
赋给newObject
中新建的屏蔽属性name
。
修改委托属性时必定要当心。惟一的办法是object.num++
。
到此简单的介绍了一下原型的基本概念。下一节将介绍原型更多的特性。