在上一節屢次提到[[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++
。
到此簡單的介紹了一下原型的基本概念。下一節將介紹原型更多的特性。