兩個物件間的原型關係和繼承有關,每個物件都可以有另一個物件做為他的原型,如此一來,前者就會繼承後者的所有特性。
如果要為物件指定「原型」,可以使用物件函式Object.create()
或Object.setPrototypeOf()
來指定另外一個物件來當做「原型」,而藉由此方式串起來的多個物件也被稱做「原型鏈」。
這一篇先講建立物件並指定原型的方式,物件函式Object.create()
有兩個參數,第一個是為物件指定原型,第二個為建立特性。
以下做個為物件指定原型的簡單範例
//建立a物件
var objA = {
writeName:function(){
return 'Name is ' + this.name;
},
};
//建立b物件並指定原型物件為objA(第一個參數)
//設定一個可變(writable:true)特性給objB,並指定值為'B'
var objB = Object.create(objA,{
name:{value:'B',writable:true}
});
//此時objB會繼承objA的所有特性(繼承特性),也包含了自己的特性(自有特性)
objB.writeName(); //會回傳'Name is B'
//objB的自有特性name值也可以被更改
objB.name='renameB'
objB.writeName(); //會回傳'Name is renameB'
//而被繼承而來的特性是無法被移除的
delete objB.writeName; //會回傳ture,但該特性不會被移除
objB.writeName(); //會回傳'Name is renameB'
//但是在Object.create中指定的原型和特性不會被Object.keys()所讀到
Object.keys(objB); //會得到空的陣列[],不會取得任何鍵值
//如果我們在objB上新增一個和objA物件中的writeName同名特性
objB.writeName=function(){
return 'objBName is ' + this.name;
};
//他會以自有特性為主,如果自有特性沒有該鍵值才會向原型尋找特性
objB.writeName(); //會回傳'objBName is renameB'
//因為剛剛指定writeName特性,所以Object.keys()就可以讀到了
Object.keys(objB); //會得到["writeName"]
//這時候我們再來移除objB的writeName特性看看
delete objB.writeName; //會回傳ture,然後把objB的自有特性writeName移除
//在呼叫時就會向原型尋找到objA的特性
objB.writeName(); //會回傳'Name is renameB'
以上是一種指定原型的方式,但要為了新宣告的物件指定特性的方式太麻煩了,所以我們通常會只建立一個指定原型的物件,之後再來手動建立特性。
//先建立一個指定原型為物件B的新物件
var objC = Object.create(objB);
//之後在手動加入特性
objC.age=20;
//手動建立的特性就可以被Object.keys()取得,但繼承而來的鍵值依舊不會顯示
Object.keys(objC); //會回傳["age"]
//而因為他繼承自objB,objB又繼承自objA,所以objC會同時有這兩個物件的特性
objC.writeName(); //會回傳'Name is renameB'
由上面的範例可以知道經過繼承的物件會有「原型」、「自有特性」、「繼承特性」這三種,那我們要如何讀取一個物件的這些資訊呢?
首先我們用Object.getPrototypeOf()
來呼叫原型物件。
Object.getPrototypeOf(objC); //會回傳objB的物件內容
Object.getPrototypeOf(objC) === objB //回傳true,因為他直接回傳objB的物件,所以兩個是相等的
//其實物件內還有一個特殊特性__proto__他也可以直接取得該物件的原型
objC.__proto__ === objB //回傳true
//也有函式可以直接檢查一個物件與另一個物件是否有在相同的原型鏈上Object.isPrototypeOf(obj);
//來試著判斷objB是否為objC的原型
objB.isPrototypeOf(objC); //會回傳true
objA.isPrototypeOf(objC); //因為objA是objB的原型,objB又是objC的原型,兩人在同一個原型鏈上,所以回傳true
objC.isPrototypeOf(objB); //雖然兩者在同一個原型鏈上,但objC不是objB的原型,所以回傳false
要檢查是否含有某個「自有特性」的鍵值,可以使用hasOwnProperty()
。
//檢查objC物件中是否含有鍵值為age的自有特性
objC.hasOwnProperty('age'); //會回傳true
//檢查objC物件中是否含有鍵值為writeName的自有特性
objC.hasOwnProperty('name'); //會回傳false,因為name是屬於原型objB的特性
最後如果要檢查該特性是否包含某個「繼承特性」可以直接用in
,這做法對「自有特性」或「繼承特性」都有效。
'name' in objC; //就算'name'是繼承自原型objB的特性,還是會回傳true
'writeName' in objC; //就算是原型的原型物件的特性,還是會回傳true
'age' in objC; //自有特性也會回傳true
以上是創建新物件並指定原型物件的各種方式,還有一些關於原型的物件函式操作,如果有說明錯誤或不明白的地方,麻煩再留言告知我,我會盡速改正!!