1. 程式人生 > >js繼承之原型鏈繼承

js繼承之原型鏈繼承

lB peak new otp nta ber 是個 發現 實現

from:https://www.cnblogs.com/sarahwang/p/6870072.html

面向對象編程都會涉及到繼承這個概念,JS中實現繼承的方式主要是通過原型鏈的方法。

一、構造函數、原型與實例之間的關系

  每創建一個函數,該函數就會自動帶有一個 prototype 屬性。該屬性是個指針,指向了一個對象,我們稱之為 原型對象。什麽是指針?指針就好比學生的學號,原型對象則是那個學生。我們通過學號找到唯一的那個學生。假設突然,指針設置 null, 學號重置空了,不要慌,對象還存在,學生也沒消失。只是不好找了。

  原型對象上默認有一個屬性 constructor,該屬性也是一個指針,指向其相關聯的構造函數。

  通過調用構造函數產生的實例,都有一個內部屬性,指向了原型對象。所以實例能夠訪問原型對象上的所有屬性和方法。

  

  所以三者的關系是,每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。通俗點說就是,實例通過內部指針可以訪問到原型對象,原型對象通過constructor指針,又可以找到構造函數。

  下面看一個例子:

1 2 3 4 5 6 7 8 9 function Dog (name) { this.name = name; this.type = ‘Dog‘; } Dog.prototype.speak =
function () {   alert(‘wang‘); } var doggie = new Dog(‘jiwawa‘); doggie.speak(); //wang

  以上代碼定義了一個構造函數 Dog(), Dog.prototype 指向的原型對象,其自帶的屬性construtor又指回了 Dog,即 Dog.prototype.constructor == Dog. 實例doggie由於其內部指針指向了該原型對象,所以可以訪問到 speak方法。

技術分享圖片

  Dog.prototype 只是一個指針,指向的是原型對象,但是這個原型對象並不特別,它也只是一個普通對象。假設說,這時候,我們讓 Dog.protptype 不再指向最初的原型對象,而是另一個類 (Animal)的實例,情況會怎樣呢?

技術分享圖片

二、原型鏈

  前面我們說到,所有的實例有一個內部指針,指向它的原型對象,並且可以訪問原型對象上的所有屬性和方法。doggie實例指向了Dog的原型對象,可以訪問Dog原型對象上的所有屬性和方法;如果Dog原型對象變成了某一個類的實例 aaa,這個實例又會指向一個新的原型對象 AAA,那麽 doggie 此時就能訪問 aaa 的實例屬性和 AA A原型對象上的所有屬性和方法了。同理,新的原型對象AAA碰巧又是另外一個對象的實例bbb,這個實例bbb又會指向新的原型對象 BBB,那麽doggie此時就能訪問 bbb 的實例屬性和 BBB 原型對象上的所有屬性和方法了。

  這就是JS通過原型鏈實現繼承的方法了。看下面一個例子:

技術分享圖片
//定義一個 Animal 構造函數,作為 Dog 的父類
function Animal () {
    this.superType = ‘Animal‘;
}

Animal.prototype.superSpeak = function () {
    alert(this.superType);
}

function Dog (name) {
    this.name = name;
    this.type = ‘Dog‘;  
}
//改變Dog的prototype指針,指向一個 Animal 實例
Dog.prototype = new Animal();
//上面那行就相當於這麽寫
//var animal = new Animal();
//Dog.prototype = animal;

Dog.prototype.speak = function () {
  alert(this.type);
}
var doggie = new Dog(‘jiwawa‘);
doggie.superSpeak();  //Animal 
技術分享圖片

  解釋一下。以上代碼,首先定義了一個 Animal 構造函數,通過new Animal()得到實例,會包含一個實例屬性 superType 和一個原型屬性 superSpeak。另外又定義了一個Dog構造函數。然後情況發生變化,代碼中加粗那一行,將Dog的原型對象覆蓋成了 animal 實例。當 doggie 去訪問superSpeak屬性時,js會先在doggie的實例屬性中查找,發現找不到,然後,js就會去doggie 的原型對象上去找,doggie的原型對象已經被我們改成了一個animal實例,那就是去animal實例上去找。先找animal的實例屬性,發現還是沒有 superSpeack, 最後去 animal 的原型對象上去找,誒,這才找到。

技術分享圖片(希望這張圖對大家的理解有幫助吧。) 

  

  這就說明,我們可以通過原型鏈的方式,實現 Dog 繼承 Animal 的所有屬性和方法。

  總結來說:就是當重寫了Dog.prototype指向的原型對象後,實例的內部指針也發生了改變,指向了新的原型對象,然後就能實現類與類之間的繼承了。(但是如果在重寫原型對象之前,產生的實例,其內部指針指向的還是最初的原型對象。這個我下次再發篇文章講。)

(文章內容全都參考於《JAVASCRIPT 高級程序設計》)

js繼承之原型鏈繼承