【進階3-5期】深度解析 new 原理及模擬實現(xiàn)

定義

new 運算符創(chuàng)建一個用戶定義的對象類型的實例或具有構造函數(shù)的內置對象的實例。 ——(來自于MDN)

舉個栗子

function Car(color) {
    this.color = color;
}
Car.prototype.start = function() {
    console.log(this.color + " car start");
}

var car = new Car("black");
car.color; // 訪問構造函數(shù)里的屬性
// black

car.start(); // 訪問原型里的屬性
// black car start

可以看出 new 創(chuàng)建的實例有以下 2 個特性

  • 1、訪問到構造函數(shù)里的屬性
  • 2、訪問到原型里的屬性

注意點

ES6新增 symbol 類型,不可以使用 new Symbol(),因為 symbol 是基本數(shù)據(jù)類型,每個從Symbol()返回的 symbol 值都是唯一的。

Number("123"); // 123
String(123); // "123"
Boolean(123); // true
Symbol(123); // Symbol(123)

new Number("123"); // Number {123}
new String(123); // String {"123"}
new Boolean(true); // Boolean {true}
new Symbol(123); // Symbol is not a constructor

模擬實現(xiàn)

當代碼 new Foo(...) 執(zhí)行時,會發(fā)生以下事情:

  1. 一個繼承自 Foo.prototype 的新對象被創(chuàng)建。
  2. 使用指定的參數(shù)調用構造函數(shù) Foo ,并將 this 綁定到新創(chuàng)建的對象。new Foo 等同于 new Foo(),也就是沒有指定參數(shù)列表,Foo 不帶任何參數(shù)調用的情況。
  3. 由構造函數(shù)返回的對象就是 new 表達式的結果。如果構造函數(shù)沒有顯式返回一個對象,則使用步驟1創(chuàng)建的對象。

模擬實現(xiàn)第一步

new 是關鍵詞,不可以直接覆蓋。這里使用 create 來模擬實現(xiàn) new 的效果。

new 返回一個新對象,通過 obj.__proto__ = Con.prototype 繼承構造函數(shù)的原型,同時通過 Con.apply(obj, arguments)調用父構造函數(shù)實現(xiàn)繼承,獲取構造函數(shù)上的屬性(【進階3-3期】)。

實現(xiàn)代碼如下

// 第一版
function create() {
    // 創(chuàng)建一個空的對象
    var obj = new Object(),
    // 獲得構造函數(shù),arguments中去除第一個參數(shù)
    Con = [].shift.call(arguments);
    // 鏈接到原型,obj 可以訪問到構造函數(shù)原型中的屬性
    obj.__proto__ = Con.prototype;
    // 綁定 this 實現(xiàn)繼承,obj 可以訪問到構造函數(shù)中的屬性
    Con.apply(obj, arguments);
    // 返回對象
    return obj;
};

測試一下

// 測試用例
function Car(color) {
    this.color = color;
}
Car.prototype.start = function() {
    console.log(this.color + " car start");
}

var car = create(Car, "black");
car.color;
// black

car.start();
// black car start

完美!

不熟悉 apply / call 的點擊查看:【進階3-3期】深度解析 call 和 apply 原理、使用場景及實現(xiàn)

不熟悉繼承的點擊查看:JavaScript常用八種繼承方案

模擬實現(xiàn)第二步

上面的代碼已經實現(xiàn)了 80%,現(xiàn)在繼續(xù)優(yōu)化。

構造函數(shù)返回值有如下三種情況:

  • 1、返回一個對象
  • 2、沒有 return,即返回 undefined
  • 3、返回undefined 以外的基本類型

情況1:返回一個對象

function Car(color, name) {
    this.color = color;
    return {
        name: name
    }
}

var car = new Car("black", "BMW");
car.color;
// undefined

car.name;
// "BMW"

實例 car 中只能訪問到返回對象中的屬性

情況2:沒有 return,即返回 undefined

function Car(color, name) {
    this.color = color;
}

var car = new Car("black", "BMW");
car.color;
// black

car.name;
// undefined

實例 car 中只能訪問到構造函數(shù)中的屬性,和情況1完全相反。

情況3:返回undefined 以外的基本類型

function Car(color, name) {
    this.color = color;
    return "new car";
}

var car = new Car("black", "BMW");
car.color;
// black

car.name;
// undefined

實例 car 中只能訪問到構造函數(shù)中的屬性,和情況1完全相反,結果相當于沒有返回值。

所以需要判斷下返回的值是不是一個對象,如果是對象則返回這個對象,不然返回新創(chuàng)建的 obj對象。

所以實現(xiàn)代碼如下:

// 第二版
function create() {
    // 創(chuàng)建一個空的對象
    var obj = new Object(),
    // 獲得構造函數(shù),arguments中去除第一個參數(shù)
    Con = [].shift.call(arguments);
    // 鏈接到原型,obj 可以訪問到構造函數(shù)原型中的屬性
    obj.__proto__ = Con.prototype;
    // 綁定 this 實現(xiàn)繼承,obj 可以訪問到構造函數(shù)中的屬性
    var ret = Con.apply(obj, arguments);
    // 優(yōu)先返回構造函數(shù)返回的對象
    return ret instanceof Object ? ret : obj;
};

【進階3-4期】思考題解

問題:用 JS 實現(xiàn)一個無限累加的函數(shù) add,示例如下:

add(1); // 1
add(1)(2);  // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10 

// 以此類推

實現(xiàn):

function add(a) {
    function sum(b) { // 使用閉包
        a = a + b; // 累加
        return sum;
    }
    sum.toString = function() { // 重寫toString()方法
        return a;
    }
    return sum; // 返回一個函數(shù)
}

add(1); // 1
add(1)(2);  // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10 

我們知道打印函數(shù)時會自動調用 toString()方法,函數(shù) add(a) 返回一個閉包 sum(b),函數(shù) sum() 中累加計算 a = a + b,只需要重寫sum.toString()方法返回變量 a 就OK了。

參考

JavaScript 深入之 new 的模擬實現(xiàn)

MDN 之 new 運算符

MDN 之 Symbol

javascript 函數(shù) add(1)(2)(3)(4) 實現(xiàn)無限極累加

進階系列目錄

  • 【進階1期】 調用堆棧
  • 【進階2期】 作用域閉包
  • 【進階3期】 this全面解析
  • 【進階4期】 深淺拷貝原理
  • 【進階5期】 原型Prototype
  • 【進階6期】 高階函數(shù)
  • 【進階7期】 事件機制
  • 【進階8期】 Event Loop原理
  • 【進階9期】 Promise原理
  • 【進階10期】Async/Await原理
  • 【進階11期】防抖/節(jié)流原理
  • 【進階12期】模塊化詳解
  • 【進階13期】ES6重難點
  • 【進階14期】計算機網絡概述
  • 【進階15期】瀏覽器渲染原理
  • 【進階16期】webpack配置
  • 【進階17期】webpack原理
  • 【進階18期】前端監(jiān)控
  • 【進階19期】跨域和安全
  • 【進階20期】性能優(yōu)化
  • 【進階21期】VirtualDom原理
  • 【進階22期】Diff算法
  • 【進階23期】MVVM雙向綁定
  • 【進階24期】Vuex原理
  • 【進階25期】Redux原理
  • 【進階26期】路由原理
  • 【進階27期】VueRouter源碼解析
  • 【進階28期】ReactRouter源碼解析

交流

進階系列文章匯總如下,內有優(yōu)質前端資料,覺得不錯點個star。

https://github.com/yygmind/blog

我是木易楊,網易高級前端工程師,跟著我每周重點攻克一個前端面試重難點。接下來讓我?guī)阕哌M高級前端的世界,在進階的路上,共勉!

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容