Javascript原型鏈詳解
js建立之初,正值java大行其道,面向物件程式設計春風正盛,js借鑑了java的物件機制,但僅是看起來像,也就是js的建構函式,如下:
function People(age) {
this.age = age;
this.getAge = function (){return this.age};
}
var p1 = new People(20);//People的例項1
var p2 = new People(40);//People的例項2
上面的程式碼很像java了,通過new constructor()的方式,可以建立多個例項。
但上面程式碼問題是getAge方法會在每個People的例項中存在,如果例項多的話,會浪費很多空間,js採用了犧牲時間,獲取空間的方法,js引入了原型理念,將方法放入原型中:
function People(age) {
this.age = age
}
People.prototype.getAge = function () {return this.age};
本文就來總結下,如何使用建構函式來實現繼承。
場景
我們假設我們有一個父建構函式People和子建構函式Student,People有一個屬性age和一個方法getAge,Student有一個屬性num和getNum。
function People(age) { this.age = age; } People.prototype.getAge = function (){return this.age;}; function Student(num) { this.num = num; } Student.prototype.getNum = function () {return this.num;};
我們要實現是Student繼承People,這在js裡可要費一番力氣了。
預設模式
我們可以利用js的原型機制,將子建構函式的原型屬性設定為父建構函式的例項,這是js中比較常用的方式:
function Student(num) {
this.num = num;
}
Student.prototype = new People();
Student.prototype.getNum = function () {return this.num;};
var stu1 = new Student('123');
這樣做其實基本實現了我們的需求,但如果深入思考上面的方式,其實有幾個缺點:
- 子類無法繼承父類的例項屬性
- 會將父類的例項屬性,擴充套件到子類的原型上
- 修改了子類的原型屬性,會導致在stu1上獲取constructor屬性為People,而不是Student
哦!!!
借用建構函式
先來看看如何解決第一個問題,我們可以巧用js的call方法,如果你還不知道這個方法,請移步這裡。
function Student(age, num) {
People.call(this, age);
this.num = num;
}
我們在子建構函式內部,借用父建構函式,這樣就巧妙地在子類中繼承了父類的例項化屬性。這其實類似java的super關鍵字。
共享原型
再來看看如何解決第二個問題,解決這個問題,其實我們可以將子建構函式的原型更改為父建構函式的原型,而不是父建構函式的例項。
Student.prototype = People.prototype;
這樣就不會將父建構函式的例項屬性擴充套件到子建構函式的原型上了。
但這樣做會導致另一個問題,就是無法再在Student的原型上擴充套件方法了,因為會擴充套件同時會擴充套件到People的原型上。
臨時建構函式
為了解決上面引發的問題,和第三個問題。我們可以在子建構函式和父建構函式之間,加一層臨時建構函式。
function F() {
}
F.prototype = People.prototype;
Student.prototype = new F();
這樣就可以Student的原型上擴充套件子建構函式的方法,同時不影響父建構函式的原形了。
在修復一下constructor屬性就ok啦
Student.prorotype.constructor = Student;
聖盃
我們將上面的幾種方法綜合起來,程式碼看起來就像下面這樣子:
//繼承函式
function inherit(C, P) {
var F = function (){};
F.prototype = P.prototype;
C.prototype = new F();//臨時建構函式
C.prototype.constructor = C;//修復constructor
C.uper = P;//儲存超類
}
function People(age) {
this.age = age;
}
People.prototype.getAge = function (){return this.age;};
function Student(age, num) {
Student.uber.call(this, age);
this.num = num;
}
inherit(Student, People);//繼承父建構函式
Student.prototype.getNum = function () {return this.num;};