首頁  >  文章  >  web前端  >  帶你詳細解讀JS原型鏈

帶你詳細解讀JS原型鏈

不言
不言轉載
2018-10-19 14:53:553145瀏覽

這篇文章帶給大家的內容是關於帶你詳細解讀JS原型鏈,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

之前對js原型和原型鏈的理解一直覺得很繞,繞來繞去的,在看了《JavaScript高級程式設計》和各種文章之後,終於對原型和原型鏈有了初步的了解,可是還是沒有很深入的了解,今次透過以前段時間遇到的一道題,分析一下,用自己的想法進行解讀,加深自己對原型和原型鏈的理解。

一、題目

下面程式運行結果是什麼?

function Animal() {
    this.name = 'Animal';
}

Animal.prototype.changeName = function (name) {
    this.name = name;
}

function Cat() {
    this.name = 'Cat';
}

var animal = new Animal();

Cat.prototype = animal;
Cat.prototype.constructor = Cat;

var cat = new Cat();

animal.changeName('Tiger');

console.log(cat.name)

A. Animal
B. Cat  
C. Tiger  
D. 都不是

答案是B Cat

#二、解讀

1. 原型物件

無論什麼時候,只要建立了一個新函數,就會根據一組特定的規則為該函數建立一個prototype屬性,這個屬性指向函數的原型物件。在預設情況下,所有的原型物件都會自動取得一個constructor(建構子)屬性,這個屬性就是一個指向prototype屬性所在函數的指標。
下面用圖來說明
function Animal() {
    this.name = 'Animal';
}
Animal.prototype.changeName = function (name) {
    this.name = name;
}

帶你詳細解讀JS原型鏈

首先建立了一個Animal函數,Animal中含有一個prototype屬性,指向Animal Prototype,而Animal.prototype. constructor指向Animal。這時候由於name屬性是在函數中定義的,所以不在Animal Prototype中,而changeName 函數是透過Animal.prototype.changeName定義的,所以我們可以透過這種方式,在實例化多個物件時,共享原型所保存的方法。
同理,當創建了Cat函數時,也是一樣。

function Cat() {
    this.name = 'Cat';
}

帶你詳細解讀JS原型鏈

2. 建立實例

當呼叫建構函式建立一個新實例後,該實例的內部將包含一個指標(內部屬性),指向建構函數的原型物件。在ECMA-262第5版中管這個指針叫[[Prototype]]。雖然在腳本中沒有標準的方式存取[[Prototype]],但Firefox、Safari和Chrome在每個物件上都支援一個屬性__proto__。明確重要的一點,這個連接存在於實例與建構子的原型物件之間,而不是存在於實例與建構子之間。

帶你詳細解讀JS原型鏈

// 将Cat的原型对象指向animal实例,获得animal中的属性,原有的属性丢失
Cat.prototype = animal;

帶你詳細解讀JS原型鏈

#這一部分相當於是把Cat的原型物件的指標指向了animal實例,所以原來Cat原型物件中的constructor屬性遺失,替換成了animal實例中的屬性,包括name屬性以及__proto__內部屬性,同時__proto__屬性也指向Animal.prototype,因此Cat也可以通過原型鏈查找呼叫到Animal中的屬性和方法。

// 相当于重新创建了constructor,指向Cat构造函数
Cat.prototype.constructor = Cat;

帶你詳細解讀JS原型鏈

這一部分相當於是重新在原型物件中建立了一個constructor屬性,同時指向Cat建構子。

var cat = new Cat();   // 实例化一个Cat对象,跟实例化Animal相似

帶你詳細解讀JS原型鏈

3. 呼叫方法

animal.changeName('Tiger');

當var animal = new Animal();實例化了一個Animal物件後, animal都包含一個內部屬性,該屬性指向了Animal.prototype;換句話說,animal與構造函數Animal沒有直接的關係。可是,可以看到雖然在實例中不含changeName,但我們可以呼叫animal.changeName(name),這是透過尋找物件屬性的過程來實現的,即:

首先尋找實例中實例中animal是否有changeName方法,如果沒有則繼續尋找,去到Animal.prototype尋找是否有changeName方法,如果有則調用,沒有則繼續尋找,到Object.prototype中尋找,最後沒找到則會返回一個null。

很明顯,在這裡實例animal中沒有changeName方法,所以需要到Animal.prototype尋找changeName方法,並呼叫成功修改了實例animal中的name屬性,為Tiger。

這個時候由於Cat.prototype是指向實例animal的,因此Cat.prototype中的name屬性也變成Tiger。

帶你詳細解讀JS原型鏈

console.log(cat.name)  // Cat

最后,获取cat.name,与查找方法同样,也是先去实例中cat查找是否含有name属性,在这里很明显是存在的,因此直接结束寻找,此时cat.name = 'Cat'。

三、总结

通过这道题,加深了我对原型和原型链的理解,其实这道题也可以扩展到关于继承的知识点,在JavaScript中实现继承主要是依靠原型链来实现。

以上是帶你詳細解讀JS原型鏈的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除