模擬實現new關鍵字

new作了什麼

要實現new關鍵字, 咱們首先要知道new都作了什麼事情, 紅寶書(JavaScript高級程序設計第三版)第145頁中(沒錯我翻書了, ^_^)是這樣描述的:app

  • 建立一個對象
  • 將構造函數的做用域賦給新對象(所以this就指向了這個新對象)
  • 執行構造函數中的代碼(爲這個新對象添加屬性)
  • 返回新對象

對於已經瞭解的人來講,上面的四步沒有任何問題,描述也準確無誤, 可是我一個萌新上來就說這個? 建立一個對象? 是個什麼樣對象? 做用域賦給新對象?... 函數

懵逼

下面咱們就結合實際的例子來一塊兒理解一下,這四步到底作了什麼ui

咱們先看咱們平時定義構造函數以及調用構造函數的姿式, 而後嘗試從結果反推new的執行過程this

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayHi = function() {console.log('My name is great ', this.name)};
const p = new Person('Alex', 22); // {name: 'Alex', age: 22} 
複製代碼

首先定義了一個Person構造函數, 該構造函數有兩個參數(name, age)以及定義在原型上的一個方法sayHi, 使用new關鍵字執行了Person後, 咱們獲得了一個對象P, 而且p上面有 name 以及 age 兩個屬性; 同時咱們也發現, p能夠訪問到原型上的 sayHi方法, 基於咱們看到的現象咱們下面一步步模擬new的執行過程spa

js中咱們沒法模擬new的執行方式, 咱們可使用函數來模擬實現過程, 由於new的時候執行了構造函數, 因此咱們第一個參數傳入一個構造函數constructor, 後面的參數做爲constructor執行時須要的參數:prototype

let myNew = function (constructor) {
   
}
複製代碼

new執行後獲得的是一個對象, 因此咱們的函數執行後也要返回一個對象:設計

let myNew = function (constructor) {
    // 建立一個對象
    let instance = {};
    // 返回新對象
    return instance;
}
複製代碼

返回出來的instance就至關於咱們示例中的p, p能夠訪問構造函數上的原型, 因此咱們須要將建立的對象instance__proto__屬性指向構造函數的原型,這樣獲得的instance才能訪問到構造函數的原型(這一步不大清楚的同窗能夠先看看原型鏈的概念)rest

let myNew = function (constructor) {
    // 建立一個對象
    let instance = {};
    instance.__proto__ = constructor.prototype;
    // 返回新對象
    return instance;
}
console.log(myNew(Person, 'Alex', 22)) //{}
複製代碼

到了這裏, 咱們已經能理解第一步(建立一個對象),和第四步了(返回新對象), 但當前實現的myNew執行之後返回的是仍舊是一個空對象, 和咱們預期的不符,缺乏了必要的屬性nameage, 咱們繼續觀察一下構造函數code

function Person(name, age) {
    this.name = name;
    this.age = age;
}
複製代碼

傳入的參數最終都賦值給了構造函數this的屬性, 若是能將將this改爲instance不就能夠了嗎? 使用apply改變this的指向cdn

let myNew = function (constructor) {
    let restArgs = Array.prototype.slice.call(arguments, 1);

    // 建立一個對象
    let instance = {};
    instance.__proto__ = constructor.prototype;

    // 將構造函數的做用域賦給新對象(所以this就指向了這個新對象) && 執行構造函數中的代碼(爲這個新對象添加屬性)
    constructor.apply(instance, restArgs);

    // 返回新對象
    return instance;
}
console.log(myNew(Person, 'Alex', 22)) // {name: 'Alex', age: 22}
複製代碼

而後咱們再稍加整理一下, 完整的代碼以下

let myNew = function (constructor) {
    if (!(constructor instanceof Function)) { throw new Error('arguments[0] require a function')};
    let restArgs = Array.prototype.slice.call(arguments, 1);

    // 建立一個對象
    let instance = {};
    instance.__proto__ = constructor.prototype;

    // 將構造函數的做用域賦給新對象(所以this就指向了這個新對象) && 執行構造函數中的代碼(爲這個新對象添加屬性)
    let result = constructor.apply(instance, restArgs);

    // 返回新對象
    return result instanceof Object ? result : instance;
}
console.log(myNew(Person, 'Alex', 22)) // {name: 'Alex', age: 22}
複製代碼

至此,咱們就徹底模擬了new建立的整個過程!過程當中有錯誤的地方,歡迎評論留言!

相關文章
相關標籤/搜索