new操做符原理及實現

[toc]javascript

分析:new作了什麼

new操做符經過執行自定義構造函數或者js內置構造函數,從而生成一個實例對象。java

mdn上把內部操做大概分爲4步:面試

  1. 建立一個空的簡單JavaScript對象(即{ } );
  2. 連接該對象(即設置該對象的構造函數)到另外一個對象 ;
  3. 將步驟1新建立的對象做爲this的上下文 ;
  4. 若是該函數沒有返回對象,則返回this。

經過一個簡單的demo感覺下上面的步驟segmentfault

function Person (name){
    this.name = name
}
let p = new Person('jack');
console.log(`p:`, p); // { name: 'jack' }
console.log(`p.__proto__===Person.prototype:`,p.__proto__===Person.prototype);  //true

能夠看到new操做符執行Person構造函數後,返回了一個內部建立的新對象,而且以這個對象爲上線文環境執行了一遍Person函數,最後將其返回,同時對象p的原型屬性指向構造函數的原型,這樣也就保證了實例可以訪問在構造函數原型中定義的屬性和方法數組

上面的demo中構造函數是沒有返回值的,若是說構造函數有返回值呢,以下app

function Person (name){
    this.name = name;
    return {age: 18}
}
let p = new Person('jack');
console.log(`p:`, p); // { age: 18 }

若是構造函數最後返回了一個對象,就會直接將其返回,而不是內部建立的新對象。函數

通過測試發現,除了返回對象,若是返回其餘類型,只要最後返回的類型爲引用類型object或者function(Function,Object,Array,Date,Error,Regexp,要排除null,由於typeof null === 'object')就會直接將其返回,而其餘基本類型都會返回內部新建立的對象。post

自定義實現

這裏咱們嘗試經過封裝一個myNew方法模擬new操做符的主要功能:接受若干參數,第一個參數爲構造函數ctr,其他爲構造器所需參數,myNew(ctr, arg1, arg2,...)測試

第一步

這裏的第一步把mdn中的一、2步放在了一塊兒:建立一個新對象,並將其__proto__屬性指向構造函數的prototype屬性this

function myNew(ctr) {
    let obj = Object.create(ctr.prototype);
}

也可使用以下方法

function myNew (ctr){
    let obj = {};
    obj.__proto__ = ctr.prototype;
}

第二步

獲取到參數以後,之內部新建立的對象obj爲上線文執行構造函數,做用是爲obj賦值

function myNew(ctr) {
    let obj = Object.create(ctr.prototype);
    
    const args = [].slice.call(arguments, 1);
    let result = ctr.apply(obj, args);
    console.log(`obj:`,obj);
}

上面的const args = [].slice.call(arguments, 1);用於將arguments類數組轉爲數組並獲取參數,也能夠經過Array.form(arguments).slice(1)或者[...arguments].slice(1)實現。

第三步

對執行構造函數後的返回值result作兼容處理。
若是構造函數最終返回對象、函數、數組、日期等其餘引用類型及Symbol,會將其直接返回,其餘基本類型及nullundefined會返回內部新建立的對象實例。

function myNew(ctr) {
    let obj = Object.create(ctr.prototype);

    const args = [].slice.call(arguments, 1);
    let result = ctr.apply(obj, args);

    var isObj = (typeof result === 'object' && result !== null);
    var isFn = typeof result === 'function';
    return (isObj || isFn) ? result : obj;
}

測試

最後,簡單測試一下
沒有返回值

function Person(name) {
    this.name = name;
}
let p = myNew(Person,'jack');
console.log(`p:`,p);

有返回值

function Person(name) {
    this.name = name;
    return {age: 33}
}
let p = myNew(Person,'jack');
console.log(`p:`,p);

參考

面試官問:可否模擬實現JS的new操做符
手動實現一個new操做符理解

相關文章
相關標籤/搜索