一名【合格】前端工程師的自檢清單 - 答案版(原型和原型鏈)

話很少說,直接開始正題吧.今天就是JavaScript基礎篇第二部分 - 原型和原型鏈javascript

原文地址: 一名【合格】前端工程師的自檢清單前端

1.理解原型設計模式以及JavaScript中的原型規則

  • 原型模式:

是指原型實例指向建立對象的種類,並經過拷貝這些原型建立新的對象,是一種用來建立對象的模式,也就是建立一個對象做爲另外一個對象的prototype屬性。java

  • 原型規則:
  1. 全部的引用類型(數組、對象、函數),都具備對象特徵,便可自由擴展屬性;
  2. 全部的引用類型,都有一個_proto_屬性(隱式原型),屬性值是一個普通對象;
  3. 全部函數,都具備一個prototype(顯示原型),屬性值也是一個普通原型;
  4. 全部的引用類型(數組、對象、函數),其隱式原型指向其構造函數的顯式原型;(obj.proto === Object.prototype)
  5. 當試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼會去它的_proto_(即它的構造函數的prototype)中去尋找;

2.instanceof的底層實現原理,手動實現一個instanceof

instanceof的底層實現原理:es6

我我的理解是從當前引用的proto一層一層順着原型鏈往上找,可否找到對應的prototype。找到了就返回true設計模式

手動實現: 如下這段代碼應該是最基礎的instanceof的實現代碼了數組

function instance_of(L, R) {//L 表示左表達式,R 表示右表達式 
    var O = R.prototype;   // 取 R 的顯示原型 
    L = L.__proto__;  // 取 L 的隱式原型
    while (true) {    
    	if (L === null)      
    	    return false;   
    	if (O === L)  // 當 O 顯式原型 嚴格等於 L隱式原型 時,返回true
    	    return true;   
    	L = L.__proto__;  
    }
}
複製代碼

3.實現繼承的幾種方式以及他們的優缺點

1. 原型鏈繼承:

Cat.prototype = new Animal();瀏覽器

特色:babel

  • 很是純粹的繼承關係,實例是子類的實例,也是父類的實例
  • 父類新增原型方法/原型屬性,子類都能訪問到
  • 簡單,易於實現

缺點:前端工程師

  • 要想爲子類新增屬性和方法,必需要在new Animal()這樣的語句以後執行,不能放到構造器中
  • 沒法實現多繼承
  • 來自原型對象的引用屬性是全部實例共享的
  • 建立子類實例時,沒法向父類構造函數傳參

2. 構造繼承:

function Cat(name){
    Animal.call(this); 
    this.name = name || 'Tom';
}
複製代碼

特色:閉包

  • 解決了原型鏈繼承中,子類實例共享父類引用屬性的問題
  • 建立子類實例時,能夠向父類傳遞參數
  • 能夠實現多繼承(call多個父類對象)

缺點:

  • 實例並非父類的實例,只是子類的實例
  • 只能繼承父類的實例屬性和方法,不能繼承原型屬性/方法
  • 沒法實現函數複用,每一個子類都有父類實例函數的副本,影響性能

3. 實例繼承

function Cat(name){ 
    var instance = new Animal();
    instance.name = name || 'Tom'; 
    return instance;
}
複製代碼

特色:

  • 不限制調用方式,無論是new 子類()仍是子類(),返回的對象具備相同的效果

缺點:

  • 實例是父類的實例,不是子類的實例
  • 不支持多繼承

4. 拷貝繼承

function Cat(name){ 
    var animal = new Animal(); 
    for(var p in animal){ 
    	Cat.prototype[p] = animal[p]; 
    } 
    Cat.prototype.name = name || 'Tom';
}
複製代碼

特色:

  • 支持多繼承

缺點:

  • 效率較低,內存佔用高(由於要拷貝父類的屬性)
  • 沒法獲取父類不可枚舉的方法(不可枚舉方法,不能使用for in訪問到)

5. 組合繼承

function Cat(name){
    Animal.call(this); 
    this.name = name || 'Tom'; 
} 
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
複製代碼

特色:

  • 彌補了方式2的缺陷,能夠繼承實例屬性/方法,也能夠繼承原型屬性/方法
  • 既是子類的實例,也是父類的實例
  • 不存在引用屬性共享問題
  • 可傳參
  • 函數可複用

缺點:

  • 調用了兩次父類構造函數,生成了兩份實例(子類實例將子類原型上的那份屏蔽了)

6. 寄生繼承

function Cat(name){ 
    Animal.call(this); 
    this.name = name || 'Tom';
}

(function(){ 
    // 建立一個沒有實例方法的類
    var Super = function(){};
    Super.prototype = Animal.prototype;
    //將實例做爲子類的原型
    Cat.prototype = new Super();
})();

Cat.prototype.constructor = Cat;
複製代碼

特色:

  • 堪稱完美

缺點:

  • 實現較爲複雜

7. Class繼承

class Cat extends Animals {}

特色:

  • 堪稱完美

缺點:

  • es6語法糖,須要必定的瀏覽器兼容性或者polyfill

4.至少說出一種開源項目(如Node)中應用原型繼承的案例

這個案例就不一一列舉了,感興趣的同窗能夠自行查閱開源項目源碼.

5.能夠描述new一個對象的詳細過程,手動實現一個new操做符

如下答案轉載自MDN - new 運算符:

new 運算符建立一個用戶定義的對象類型的實例或具備構造函數的內置對象的實例。new 關鍵字會進行以下的操做:

  1. 建立一個空的簡單JavaScript對象(即{});
  2. 連接該對象(即設置該對象的構造函數)到另外一個對象 ;
  3. 將步驟1新建立的對象做爲this的上下文 ;
  4. 若是該函數沒有返回對象,則返回this。
//本身定義的new方法
let newMethod = function (Parent, ...rest) {
    // 1.以構造器的prototype屬性爲原型,建立新對象;
    let child = Object.create(Parent.prototype);
    // 2.將this和調用參數傳給構造器執行 
    Parent.apply(child, rest);
    // 3.返回第一步的對象
    return child;
};
複製代碼

6.理解es6 class構造以及繼承的底層實現原理

ES6類的底層仍是經過構造函數以及原型鏈繼承實現,具體實現能夠參考babel編譯成的es5代碼實現.

系列連接:

相關文章
相關標籤/搜索