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

更新:謝謝你們的支持,最近折騰了一個博客官網出來,方便你們系統閱讀,後續會有更多內容和更多優化,猛戳這裏查看javascript

------ 如下是正文 ------html

定義

new 運算符建立一個用戶定義的對象類型的實例或具備構造函數的內置對象的實例。 ——(來自於MDN)前端

舉個栗子java

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

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

car.start(); // 訪問原型裏的屬性
// black car start
複製代碼

能夠看出 new 建立的實例有如下 2 個特性webpack

  • 一、訪問到構造函數裏的屬性
  • 二、訪問到原型裏的屬性

注意點

ES6新增 symbol 類型,不可使用 new Symbol(),由於 symbol 是基本數據類型,每一個從Symbol()返回的 symbol 值都是惟一的。git

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
複製代碼

模擬實現

當代碼 new Foo(...) 執行時,會發生如下事情:github

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

模擬實現第一步

new 是關鍵詞,不能夠直接覆蓋。這裏使用 create 來模擬實現 new 的效果。web

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

實現代碼以下算法

// 初版
function create() {
	// 建立一個空的對象
    var obj = new Object(),
	// 得到構造函數,arguments中去除第一個參數
    Con = [].shift.call(arguments);
	// 連接到原型,obj 能夠訪問到構造函數原型中的屬性
    obj.__proto__ = Con.prototype;
	// 綁定 this 實現繼承,obj 能夠訪問到構造函數中的屬性
    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 原理、使用場景及實現

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

模擬實現第二步

上面的代碼已經實現了 80%,如今繼續優化。

構造函數返回值有以下三種狀況:

  • 一、返回一個對象
  • 二、沒有 return,即返回 undefined
  • 三、返回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 中只能訪問到構造函數中的屬性,和狀況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 中只能訪問到構造函數中的屬性,和狀況1徹底相反,結果至關於沒有返回值。

因此須要判斷下返回的值是否是一個對象,若是是對象則返回這個對象,否則返回新建立的 obj對象。

因此實現代碼以下:

// 第二版
function create() {
	// 建立一個空的對象
    var obj = new Object(),
	// 得到構造函數,arguments中去除第一個參數
    Con = [].shift.call(arguments);
	// 連接到原型,obj 能夠訪問到構造函數原型中的屬性
    obj.__proto__ = Con.prototype;
	// 綁定 this 實現繼承,obj 能夠訪問到構造函數中的屬性
    var ret = Con.apply(obj, arguments);
	// 優先返回構造函數返回的對象
	return ret instanceof Object ? ret : obj;
};
複製代碼

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

問題:用 JS 實現一個無限累加的函數 add,示例以下:

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

// 以此類推
複製代碼

實現:

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

add(1); // 1
add(1)(2);  // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10 
複製代碼

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

參考

JavaScript 深刻之 new 的模擬實現

MDN 之 new 運算符

MDN 之 Symbol

javascript 函數 add(1)(2)(3)(4) 實現無限極累加

進階系列目錄

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

交流

進階系列文章彙總以下,內有優質前端資料,以爲不錯點個star。

github.com/yygmind/blo…

我是木易楊,網易高級前端工程師,跟着我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!

相關文章
相關標籤/搜索