JavaScript 作為一門面對對象語言,但是卻不支持接口繼承课竣,只支持實現(xiàn)繼承嘉赎。JavaScript 中實現(xiàn)繼承就是通過原型鏈來實現(xiàn)。
簡介
首先了解構造函數(shù)于樟、原型對象和實例之間的關系公条。
- 通過 new 操作符加構造函數(shù)的方式可以創(chuàng)建實例。
- 構造函數(shù)具有相應的原型對象迂曲。
- 在原型對象中定義的原型方法在所有實例中都能夠訪問到靶橱。
原理
接下來了解它們之間的關系是如何構建的。
構造函數(shù)
在 JavaScript 中聲明構造函數(shù)的時候路捧,都會為這個函數(shù)創(chuàng)建一個 prototype 屬性关霸。
//聲明構造函數(shù)
function Person(name) {
this.name = name;
}
在瀏覽器下看 Person 構造函數(shù)函數(shù)就是以上這樣,prototype 屬性指向了這個構造函數(shù)的原型對象杰扫。
(實際上聲明任何函數(shù)都會有這個屬性队寇,只不過在構造函數(shù)中這個屬性才能夠發(fā)揮作用)
原型對象
聲明構造函數(shù)之后會為這個構造函數(shù)創(chuàng)建一個原型對象,為這個原型對象寫入屬性就可以在實例中訪問到章姓。
//聲明構造函數(shù)
function Person(name) {
this.name = name;
}
//為原型對象寫入屬性和方法
Person.prototype.age = 18;
Person.prototype.sayName = function() {
console.log(this.name);
};
在瀏覽器中展示如下
在瀏覽器中可以清楚地看到它的內(nèi)容佳遣,其中 constructor 屬性指回了構造函數(shù)本身。constructor 屬性和 prototype 屬性構建起了構造函數(shù)和原型對象之間的相互關系啤覆。
實例
用 new 操作符可以進行實例化苍日,創(chuàng)建這個構造函數(shù)的一個實例惭聂。
//聲明構造函數(shù)
function Person(name) {
this.name = name;
}
//為原型對象寫入屬性和方法
Person.prototype.age = 18;
Person.prototype.sayName = function() {
console.log(this.name);
};
//實例化
var person1 = new Person("demonly");
new 操作符的作用就是首先新建一個對象窗声,然后在這個對象中執(zhí)行構造函數(shù)中的代碼,最終返回這個對象辜纲。
依然還是在瀏覽器中看這個對象笨觅。
其中包含有構造函數(shù)寫入的 name 屬性和一個 _proto_ 屬性。 _proto_ 屬性指向這個實例的構造函數(shù)的原型對象耕腾,這是由瀏覽器生成的一個內(nèi)部屬性见剩,無法被訪問也無法修改。
由實例訪問原型對象中的屬性和方法就是通過 _proto_ 屬性扫俺。當訪問一個屬性或者方法時苍苞,首先會尋找實例中是否存在這個屬性,如果沒有的話就會到 _proto_ 指向的原型對象中尋找狼纬。因此只需要在原型對象中添加屬性和方法就讓這個構造函數(shù)創(chuàng)建的所有實例都能訪問到羹呵,包括在添加屬性之前實例化的實例。
為了更加直觀地表現(xiàn)它們的關系畫了這樣一張圖疗琉。
原型鏈
原型鏈的實現(xiàn)就是重寫構造函數(shù)的 prototype 屬性的指向盈简,從而使得瀏覽器在實例中訪問屬性的時候能夠在自定義的對象中找到凑耻。
我們現(xiàn)在再加入一個構造函數(shù)太示。
//聲明構造函數(shù)
function Person(name) {
this.name = name;
}
//新建構造函數(shù)
function Animal() {};
//為新類型的原型對象寫入方法
Animal.prototype.breath = function() {
console.log("i can breath");
}
//繼承
Person.prototype = new Animal();
//為原型對象寫入屬性和方法
Person.prototype.sayName = function() {
console.log(this.name);
};
//實例化
var person1 = new Person("demonly");
繼承的實現(xiàn)部分我們改寫了 Person 構造函數(shù)的 prototype 的指向,使它指向了 Animal 類型的一個實例香浩。在瀏覽器中我們也可以看到 _proto_ 屬性的指向變成了 Animal 的實例类缤。
我們在 Person 類型的實例中也可以訪問 Animal 類型的方法。
當我們調(diào)用 breath 方法的時候邻吭,瀏覽器會首先在這個實例本身中尋找 breath 方法呀非,沒有找到就轉(zhuǎn)為到 _proto_ 所指向的 Person 的原型對象(即 Animal 類型的實例)中尋找,依然沒有找到就轉(zhuǎn)為到 _proto_ 所指向的 Animal 的原型對象中尋找镜盯。
原型鏈就是這樣通過 _proto_ 屬性的逐級連接實現(xiàn)的岸裙。如果再改寫 Animal 的原型對象,那么就可以再增加原型鏈的長度速缆。
這里需要注意的是降允,重寫原型對象僅僅改變了 Person 的prototype 屬性的指向,而在這之前寫入的原型方法都還會留在原來的原型對象上艺糜,在這之前實例化的實例的 _proto_ 屬性也依然會指向原來的原型對象剧董。