原型鏈知識記錄

此貼用於記錄原型鏈相關的一些東西。
在JavaScript裏,函數都有prototype,對象都有__proto__,一個函數的prototype和一個對象的__proto__就是原型,原型其實也是一個對象。
一個函數的prototype和這個函數的示例對象的__proto__是同一個引用,即:es6

function A(){
}

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

當調用某一個對象的方法的時候,若是找不到這個對象的方法,則會去找這個對象的原型裏的方法,若是再找不到,則繼續找原型的原型的方法,一直往上找,這個就是原型鏈,若是都找不到,則會報錯。函數

若是原型鏈很長,當訪問某個屬性的時候,會話更多的時間,須要注意原型鏈不要太長。性能

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

一. 原型鏈繼承

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

上述例子中,b竟然是A的實例對象,也是B的實例對象,這樣實際上是有問題的,須要在B.prototype = new A('');後面加上:prototype

B.prototype.constructor = B;

優勢:
1.簡單,實現容易
2.能訪問父類全部的方法和屬性code

缺點:
1.要給B的prototype新增方法必需要在 new A('');以後
2.沒法實現多繼承
3.無法向父類的構造函數傳遞參數
4.實例是當前類的實例,也是父類的實例對象

二. 構造繼承

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.調用了兩次父類構造函數,生成了兩份實例(子類實例將子類原型上的那份屏蔽了)ip

四.寄生組合繼承

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.實現複雜原型鏈

順便加一個知識點,當使用new關鍵字來建立對象的時候,也用到了原型相關的知識,因此也記在了這裏;
假設有一個function A(name){ this.name = name; };使用new關鍵字建立一個A的實例對象let a = new A();
new關鍵字主要作了如下幾個步驟:
(1)建立一個空的對象
var obj = {};
(2)將這個對象的原型__proto__指向A的prototype
obj.__proto__ = A.prototype
(3)函數A的this指向obj;接着執行A函數
(4)將obj返回,變量a指向obj

本文主要是參考了別人的文章,寫一些本身的理解,若是有什麼寫錯的地方歡迎大神來指正一下

相關文章
相關標籤/搜索