上回介紹了如何使用建構式來建立原型,接著今天介紹使用 Object.create()
建立多層原型,先前在 T Shirt 例子有提到 , T Shirt 的原型是衣服,而衣服原型,仍然能有原型,這種多層原型就會使用 Object.create()
這個方法。
Object.create()
這個功能簡單來說就是,建立一個新的原型物件,而這個原型物件是沒有實體的,這邊使用範例來觀察
const Ryder = {
name:'Ryder'
}
const student = Object.create(Ryder)
console.log(student) //{}
student.name // Ryder
雖然 student
實體仍是空物件,但我們使用 student.name
可以看到他會回傳 Ryder
這是因為 Object.create()
的功能,讓 student
的原型繼承 Ryder
的內容 ,可以點開 student
回傳的空物件,看看他 [[Prototype]]
的結構:
這樣便清楚 Object.create()
的功能其實就是能夠讓參數中的內容,成為目標的原型。
這邊複製之前的 TShirt
程式碼
function TShirt(color,material,size){
this.color = color
this.material = material
this.size = size
}
TShirt.prototype.clothe = function() {
console.log(`穿上 ${this.color} T Shit`);
}
const BlackTShit = new TShirt('black','棉','L')
目前是的原型鍊是
BlackTShit (子層) ⇒ TShirt (父層)
預期在 TShirt 上一層在新增一層 apparel
原型,也就是讓原型鍊變成
BlackTShit ⇒ TShirt ⇒ apparel (服飾)
接下來就是新增 apparel
原型,並使用剛剛介紹的 Object.create()
, 讓 apparel
成為 TShirt
的原型,以下是範例:
// 新的一層原型
function apparel(type){
this.type = type || 'T Shirt'
}
// 為新原型新增方法
apparel.prototype.mirror = function(){
console.log(`我穿著 ${this.color} 的 ${this.type} `)
}
function TShirt(color,material,size){
this.color = color
this.material = material
this.size = size
}
// 使用 Object.create 讓 TShirt 原型繼承 apparel 原型
TShirt.prototype = Object.create(apparel.prototype)
TShirt.prototype.clothe = function() {
console.log(`穿上 ${this.color} T Shit`);
}
const BlackTShit = new TShirt('black','棉','L')
在這段範例中要特別注意的是這一段:
TShirt.prototype = Object.create(apparel.prototype)
這邊是讓 TShirt
這個函式建構式的原型,繼承了 apparel
函式就建構式的原型。
在完成上面使用 Object.create()
串連原型後,接著試者使用 mirror()
以及 clothe()
方法。
BlackTShit.clothe() //穿上 black T Shit
BlackTShit.mirror() //我穿著 black 的 undefined
可以發現在 apparel
這一層原型新增的方法雖然成功了, mirror()
方法中的 type
屬性卻無法正確顯示,這是因為使用 Object.create()
讓 TShirt
繼承了 apparel
的原型,但卻沒有繼承 apparel
的『建構函式』。
這時候需要在 TShirt
建構函式中使用 call()
方法,來讓 TShirt
中的 this
綁定到 apparel
上,而這段其實就是將兩個 建構函式 串接起來,並在傳入 'T Shirt'
字串當作 apparel
的 type
參數。
function apparel(type){
this.type = type || '帽 T'
}
apparel.prototype.mirror = function(){
console.log(`我穿著 ${this.color} 的 ${this.type} `)
}
function TShirt(color,material,size){
apparel.call(this, 'T Shirt')
this.color = color
this.material = material
this.size = size
}
TShirt.prototype = Object.create(apparel.prototype)
TShirt.prototype.constructor = TShirt
TShirt.prototype.clothe = function() {
console.log(`穿上 ${this.color} T Shit`);
}
const BlackTShit = new TShirt('black','棉','L')
BlackTShit.mirror()
BlackTShit.clothe()
這樣在 console 打上 BlackTShit
便會看到,來自 TShirt
原型 、apparel
原型上的完整屬性以及內容了。
最後這邊看起來程式碼已經相當的完整了,但是如果要讓原型鍊完整的話,其實還必須在 Object.create()
底下加上 TShirt.prototype.constructor = TShirt
,這是因為在 Object.create()
設定時就會將原本 TShirt
原型中的內容在加回來。
function apparel(type){
this.type = type || '帽 T'
}
// 為新原型新增方法
apparel.prototype.mirror = function(){
console.log(`我穿著 ${this.color} 的 ${this.type} `)
}
function TShirt(color,material,size){
apparel.call(this, 'T Shirt')
this.color = color
this.material = material
this.size = size
}
// 透過 Object.create() 為 TShirt
TShirt.prototype = Object.create(apparel.prototype)
TShirt.prototype.constructor = TShirt
TShirt.prototype.clothe = function() {
console.log(`穿上 ${this.color} T Shit`);
}
const BlackTShit = new TShirt('black','棉','L')
BlackTShit.mirror()
BlackTShit.clothe()
這樣就是完整的讓原型繼承原型的方法了。