原型 - 使用Object.create建立多層繼承

Abby 旦旦
6 min readJul 14, 2021

--

之前講到原型有繼承的特性,因此形成原型鍊,以下範例的原型鍊:

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

  1. 可以直接繼承指定的物件作為原型
  2. 在不改變屬性的情況下,所有的值都以指定的物件為預設值

以下簡易範例可以解釋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();

--

--

Abby 旦旦
Abby 旦旦

Written by Abby 旦旦

從零開始轉職網頁設計,正在緩慢朝前端工程邁進中!偶爾會發一些讀後感和UI分享,ㄚㄚ!

No responses yet