之前講到原型有繼承的特性,因此形成原型鍊,以下範例的原型鍊:
function Cat(name , color, size){
this.name = name;
this.color = color;
this.size = size;
}
var Bibi = new Cat('比比', '黃', '小');
cnosole.log(Bibi);
Bibi 的原型鍊是這樣:Object(物件原型) > Cat(函式物件原型) > Bibi(實體)
但是 Cat(函式物件原型) 如果要分類的話,應該要分出 '動物' 原型,也就是說 Bibi 的原型鍊應該要變成:
Object(物件原型) > Animal > Cat(函式物件原型) > Bibi(實體)
該怎麼建立多層繼承呢?
Object.create
- 可以直接繼承指定的物件作為原型
- 在不改變屬性的情況下,所有的值都以指定的物件為預設值
以下簡易範例可以解釋Object.create的繼承方式:
var Bibi = {
name: '比比',
color:'黃',
size:'小',
meow: function (){
console.log(this.name + '叫');
}
}var Pupu = Object.create(Bibi);
console.log(Pupu);
可以看到Pupu是一個空的物件,但是他的原型裡面有Bibi的屬性,如下圖:
當然也可以更改Pupu的name、color、size,如下圖:
Pupu.name = '噗噗';
Pupu.color = '灰';
Pupu.size = '大';console.log(Pupu);
這時Pupu就變成一個有自訂義的物件,且原型內還是保有Bibi的屬性
所以當在創造多層原型鍊的時候,就會使用Object.create。
所以回到主題,當我們要在 Cat(函式物件原型) 上一層再做分類的話,應該要分出 ‘動物’ 原型,也就是說 Bibi 的原型鍊應該要變成:
Object(物件原型) > Animal(函式物件原型) > Cat(函式物件原型) > Bibi(實體)
以下範例:
建構一個Animal的藍圖
function Animal(family){
this.kingdom = '動物界';
// 這個物件的kingdom屬性的值為'動物界'。 this.family = family || '人科';
// 這個物件的family屬性的值family,如果沒有值就顯示'人科'。
}將Animal的原型內加入alive方法,用來顯示這個物件是一種生物
Animal.prototype.alive = function(){
console.log(this.name + '是一種生物');
}
這樣我們就把Cat的上一個物件 Animal 製作出來了。
建構一個Cat的藍圖
function Cat(name , color, size){
this.name = name;
this.color = color || '灰';
this.size = size || '大';
}Cat的建構函式下的原型 繼承到 Animal的建構函式的原型
Cat.prototype = Object.create(Animal.prototype);
這邊的Cat.prototype = Object.create(Animal.prototype); 意思和前面的案例一樣,Pupu 繼承到 Bibi 的原型__proto__內容。
這時我們就可以新增一隻貓的物件叫做比比Bibi,並log他的內容:
var Bibi = new Cat('比比', '黃', '小');
console.log(Bibi);呼叫之前建構Animal的原型內加入alive方法
Bibi.alive();
也就看到原型鍊有成功變成Object > Animal > Cat > Bibi 了,呼叫之前建構Animal的原型內加入alive方法也會正常顯示 ‘比比是一種生物’。
但是會發現之前建構的Animal的藍圖裡面的屬性,並沒有顯示在Bibi的屬性裡,原因是因為Bibi只是繼承了Animal原型內的alive方法而已;
這時我們就要在 Cat 建構函式中加入 Animal 的參數,如下:
function Cat(name , color, size){
console.log(this);
// Cat {},還是空的 Animal.call(this, '貓科');
console.log(this);
// Cat {kingdom: "動物界", family: "貓科"} this.name = name;
this.color = color || '灰';
this.size = size || '大';
console.log(this);
// Cat {kingdom: "動物界", family: "貓科", name: "比比", color: "黃", size: "小"}var Bibi = new Cat('比比', '黃', '小');
因為使用 new 出一個新的 object 叫做 Bibi,而 new object 在這裡呼叫Animal,並指定 Animal 的 this 為 Cat 自己的 this,所以 new object才會具備 animal/Cat 的屬性 。
另外,因為我們先前 Cat 的建構函式下的原型 繼承到 Animal 的建構函式的原型,導致 Bibi 往下尋找是找不到 Cat 的 constructor,所以我們要把它導正回來:
Cat.prototype.constructor = Cat;
完整的js
function Animal(family) {
this.kingdom = '動物界';
this.family = family || '人科';
}Animal.prototype.alive = function () {
console.log(this.name + '是一種生物');
}function Cat(name, color, size) {
Animal.call(this, '貓科');
this.name = name;
this.color = color || '灰';
this.size = size || '大';
}Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;var Bibi = new Cat('比比', '黃', '小');
console.log(Bibi);
Bibi.alive();