原型鏈的理解

上帝遺忘之子發表於2019-02-17

此貼用於記錄原型鏈相關的一些東西。
函式都有prototype,物件都有__proto__,一個函式的prototype和一個物件的__proto__就是原型,原型其實也是一個物件。
一個函式的prototype和這個函式的示例物件的__proto__是同一個引用,即:

function A(){
}

let a = new A();
console.log(a.__proto__ === A.prototype); //返回的是true

當呼叫某一個物件的方法的時候,如果找不到這個物件的方法,則會去找這個物件的原型裡的方法,如果再找不到,則繼續找原型的原型的方法,一直往上找,這個就是原型鏈,如果都找不到,則會報錯。

在引入es6的extends之前,函式的繼承就是通過原型來實現的。下面記錄一下我理解的繼承,下面的繼承參考的是[JS繼承的實現方式
][1],他那裡有完整的繼承方法。

一. 原型鏈繼承

function A(name) {
  this.name = name;
  this.f1 = function () {
    console.log(this.name + `正在做f1`);
  }
}
A.prototype.f2 = function () {
  console.log(this.name + `正在做f2`);
}

function B(name) {
  this.name = name;
}

B.prototype = new A(``);

let b = new B(`test`);
b.f1();
b.f2();
console.log(b.__proto__ === B.prototype); //true

console.log(b instanceof A);//true
console.log(b instanceof B);//true

優點:
1.簡單,實現容易
2.能訪問父類所有的方法和屬性
3.例項是當前類的例項,也是父類的例項

缺點:
1.要給B的prototype新增方法必須要在 new A(“);之後
2.無法實現多繼承
3.沒法向父類的建構函式傳遞引數

二. 構造繼承

function A(name){
  this.name = name;
  this.f1 = function(){
    console.log(this.name + `正在做f1`);
  }
}
A.prototype.f2 = function(){
  console.log(this.name + `正在做f2`);
}

function B(name) {
  A.call(this, name);
}

let b = new B(`test`);
b.f1();
// b.f2();// 會報錯

console.log(b instanceof A);//false
console.log(b instanceof B);//true

優點:
1.可以使用父類的屬性和方法
2.可以實現多重繼承,即可以call多個函式
3.例項物件是當前類的例項,不是父類的例項
缺點:
1.無法獲取A的prototype的屬性和方法
2.只是子類的例項,不是父類的例項
3.無法實現函式複用,每個子類都有父類例項函式的副本,影響效能(此處應該是呼叫call的時候會生成父類的例項副本,具體的還得再研究研究)

三. 組合繼承

function A(name){
  this.name = name;
  this.f1 = function(){
    console.log(this.name + `正在做f1`);
  }
}
A.prototype.f2 = function(){
  console.log(this.name + `正在做f2`);
}

function B(name) {
  A.call(this, name);
}
B.prototype = new A(``);
//要修正prototype的constructor,為什麼要修正還有待研究
B.prototype.constructor = B;

let b = new B(`test`);
b.f1();
b.f2();

console.log(b instanceof A);//true
console.log(b instanceof B);//true

優點:
1.包含了原型鏈繼承和構造繼承的優點
2.解決了原型鏈繼承的無法實現多繼承和沒法向父類的建構函式傳遞引數的缺點
3.解決了構造繼承無法獲取A的prototype的屬性和方法還有隻是子類的例項,不是父類的例項的問題
缺點:
1.呼叫了兩次父類建構函式,生成了兩份例項(子類例項將子類原型上的那份遮蔽了)

四.寄生組合繼承

function A(name){
  this.name = name;
  this.f1 = function(){
    console.log(this.name + `正在做f1`);
  }
}
A.prototype.f2 = function(){
  console.log(this.name + `正在做f2`);
}

function B(name) {
  A.call(this, name);
}

(function(){
  // 建立一個沒有例項方法的類
  var Super = function(){};
  Super.prototype = A.prototype;
  //將例項作為子類的原型,這樣就可以只獲取A的prototype的屬性了
  B.prototype = new Super();
  //
  B.prototype.constructor = B;
})();

let b = new B(`test`);
b.f1();
b.f2();

console.log(b instanceof A);//true
console.log(b instanceof B);//true

優點:
1.在組合繼承的基礎上,只生成了一份父類的例項,prototype也只繼承了父類的prototype,沒有繼承私有的屬性
缺點:
1.實現複雜

相關文章