0%

JS核心-(50)-繼承與原型練-原型鏈、建構函式整體結構概念

前言

此篇要來複習「繼承與原型鍊」章節講到的觀念

整體結構概念

  • 首先,比比這隻狗是一個實體(instance),是繼承了狗的原型。

  • 狗的原型又繼承了動物的原型。

  • 動物的原型又繼承了物件的原型。

那麼動物的原型因為是比較高的階層,所以我們又可以有別的動物來繼承動物的原型,在上一張的最後則是由貓的原型來繼承

  • 貓的原型繼承了動物的原型,也有自己的方法。

  • 並且也可以透過貓的原型產生貓的實體。貓的實體也是繼承了貓的原型。


我們用下面這張表來看一下這些東西的互相關係

為了讓畫面簡潔一點,這邊沒有顯示出貓的實體以及貓的原型

畫面上有綠色字體顯示的,DogAnimalObjectFunction ,這些都是屬於建構函式

首先我們先專注在最左上方的 Bibi (狗的實體),如果我們使用 console.log 來看 Bibi 的話,就會顯示出這樣的結果,內容有包含了 namefamilycolor以及這個章節介紹的重點 __proto__

這個 __proto__ 會指向 Dog.prototype ,而 Dog.prototype 就是透過Dog的建構函式而來,所以Dog.prototypeconstructor 會指向 Dog 的建構函式。

同時 Dog.prototype__proto__ 也可以向上尋找,找到 Animal.prototype

每一個原型都會有一個 constructor 去指向建構函式,所以 Animal.prototypeconstructor 就會去指向 Animal 的建構函式

然後 Animal__proto__ 也可以向上尋找,找到 Object.prototype 的原型。

然後 Object.prototypeconstructor 也會連結到 Object 的建構函式。

如果 Object.prototype__proto__ 繼續向上尋找,最後會找到 null 的結果。

了解到這裡,其實你已經掌握了絕大部分的概念,那麼我們再多說一些觀念吧!


首先先把剛剛的部分調淡一些些。

到目前為止有 DogAnimalObject 這些建構函式,都可以利用 Dog.prototypeAnimal.prototypeObject.prototype

的方式來產生原型或是將方法掛載在這些建構函式的原型鏈上。

那麼為什麼,我們可以使用 prototype 這個方法呢?

那是因為 DogAnimalObject__proto__ 都是繼承於 Function.prototype

最後我們再來看一下函式, Function.prototype

Function.prototype__proto__ 繼續向上尋找,一樣會找到 Object.prototype

Function.prototypeconstructor 會指向 Function 的建構函式。

Function 的建構函式的 __proto__ 則會指向回 Function.prototype


用程式碼來驗證一下

function Animal (family) {
    this.kingdom = '動物界';
    this.family = family || '人科';
}

Animal.prototype.move = function () {
    console.log(this.name + ' 移動');
};

function Dog (name, color, size) {
    Animal.call(this, '犬科');
    this.name = name;
    this.color = color || '白色';
    this.size = size || '小';
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function () {
    console.log(this.name + '吠叫');
}

var Bibi = new Dog('比比', '棕色', '小');

console.log(Bibi.__proto__ === Dog.prototype);

透過這樣的結果我們可以知道這個 __proto__ 會指向狗的原型 Dog.prototype ,而狗的原型 Dog.prototype 就是透過狗的建構

函式 Dog 而來,這個觀念是對的。

那我們繼續 ~

console.log(Bibi.__proto__.__proto__ === Animal.prototype);

所以往上找兩層也就是 狗實體 Bibi.__proto__ >> 狗原型 Dog.prototype.__proto__ >> 動物原型 Animal.prototype

而答案也是 true


再來驗證建構器 constructor 的觀念

console.log(Bibi.__proto__.__proto__.constructor === Animal);

那麼動物原型的建構器很明顯就應該要是動物的建構函式,答案一樣也是 true,代表的確動物的原型的 constructor 是指向動物的建構函式。


再來驗證原型鏈的最上層會是 null

console.log(Bibi.__proto__.__proto__.__proto__.__proto__ === null);

驗證的結果也是 true


驗證所有的建構函式都是繼承 Function 的原型

console.log(Dog.__proto__ === Function.prototype);
console.log(Animal.__proto__ === Function.prototype);
console.log(Object.__proto__ === Function.prototype);


驗證 Function 的 proto 是否指向 Function 的原型

console.log(Function.__proto__ === Function.prototype);

同時,Function 的原型再向上尋找就會找到物件的原型喔!

我們繼續再加上

console.log(Function.__proto__.__proto__ === Object.prototype);

所以這個觀念也被驗證了。

以上是原型鏈以及繼承的關係,也可以參考 MDN 繼承與原型鏈 的文件