js new一個對象的過程,實現一個簡單的new方法

 

對於大部分前端開發者而言,new一個構造函數或類獲得對應實例,是很是廣泛的操做了。下面的例子中分別經過構造函數與class類實現了一個簡單的建立實例的過程。html

// ES5構造函數
let Parent = function (name, age) {
    this.name = name;
    this.age = age;
};
Parent.prototype.sayName = function () {
    console.log(this.name);
};
const child = new Parent('聽風是風', 26);
child.sayName() //'聽風是風'


//ES6 class類
class Parent {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayName() {
        console.log(this.name);
    }
};
const child = new Parent('echo', 26);
child.sayName() //echo

但new不該該像一個黑盒,咱們除了知道結果,更應該明白過程究竟如何。那麼那麼這篇文章主要圍繞兩點展開,第一,js中new一個對象時到底發生了什麼第二,知道了原理後咱們經過js來實現一個簡單的new方法前端

1、new操做中發生了什麼?es6

比較直觀的感受,當咱們new一個構造函數,獲得的實例繼承了構造器的構造屬性(this.name這些)以及原型上的屬性app

在《JavaScript模式》這本書中,new的過程說的比較直白,當咱們new一個構造器,主要有三步:函數

• 建立一個空對象,將它的引用賦給 this,繼承函數的原型。
• 經過 this 將屬性和方法添加至這個對象
• 最後返回 this 指向的新對象,也就是實例(若是沒有手動返回其餘的對象)this

咱們改寫上面的例子,大概就是這樣:spa

// ES5構造函數
let Parent = function (name, age) {
    //1.建立一個新對象,賦予this,這一步是隱性的,
    // let this = {};
    //2.給this指向的對象賦予構造屬性
    this.name = name;
    this.age = age;
    //3.若是沒有手動返回對象,則默認返回this指向的這個對象,也是隱性的
    // return this;
};
const child = new Parent();

這應該不難理解,你應該在工做中看過相似下述代碼中的操做,將this賦予一個新的變量(例如that),最後返回這個變量:prototype

// ES5構造函數
let Parent = function (name, age) {
    let that = this;
    that.name = name;
    that.age = age;
    return that;
};
const child = new Parent('聽風是風', '26');

爲何要這麼寫呢?我在前面說this的建立與返回是隱性的,但在工做中爲了讓構造過程更易可見與更易維護,因此纔有了上述使用that代替this,同時手動返回that的作法;這也驗證了隱性的這兩步確實是存在的。rest

但上述這個解釋我以爲不夠完美,它只描述了構造器屬性是如何塞給實例,沒說原型上的屬性是如何給實例繼承的。code

我在winter大神的重學前端專欄中,看到了比較符合我心意的,同時也是符合原理的描述:

• 以構造器的prototype屬性爲原型,建立新對象;
• 將this(也就是上一句中的新對象)和調用參數傳給構造器,執行;
• 若是構造器沒有手動返回對象,則返回第一步建立的對象

到這裏無論怎麼說,你都應該大概知道了new過程當中會新建對象,此對象會繼承構造器的原型與原型上的屬性,最後它會被做爲實例返回這樣一個過程。知道了原理,咱們來手動實現一個簡單的new方法。

2、實現一個簡單的new方法

// 構造器函數
let Parent = function (name, age) {
    this.name = name;
    this.age = age;
};
Parent.prototype.sayName = function () {
    console.log(this.name);
};
//本身定義的new方法
let newMethod = function (Parent, ...rest) {
    // 1.以構造器的prototype屬性爲原型,建立新對象;
    let child = Object.create(Parent.prototype);
    // 2.將this和調用參數傳給構造器執行
    let result = Parent.apply(child, rest);
    // 3.若是構造器沒有手動返回對象,則返回第一步的對象
    return typeof result  === 'object' ? result : child;
};
//建立實例,將構造函數Parent與形參做爲參數傳入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo';

//最後檢驗,與使用new的效果相同
child instanceof Parent//true
child.hasOwnProperty('name')//true
child.hasOwnProperty('age')//true
child.hasOwnProperty('sayName')//false

那麼到這裏就介紹完畢了。

new一個構造函數默認返回什麼?調用構造函數不使用new能獲得實例嗎?若是你對這些有興趣,能夠閱讀博主這篇博客:

精讀JavaScript模式(三),new一個構造函數究竟發生了什麼?

實例爲何能使用構造器prototype上的方法?繼承之間的關係又是怎麼樣的?若是你對這些有興趣,能夠閱讀博主這篇博客:

精讀JavaScript模式(八),JS類式繼承

ES6新增的class類怎麼用?與傳統構造函數寫法有哪些區別?如何快速上手class類?那你能夠閱讀博主這篇博客:

es6入門5--class類的基本用法

最後模擬實現new方法中,...rest是個什麼參數?若是你對這個存疑,那怕是得了解下ES6中的取代arguments的rest參數,歡迎閱讀這篇:

es6入門3--箭頭函數與形參等屬性的拓展

原諒個人厚顏無恥,若是你以爲本文對你有所幫助,歡迎評論,點贊與關注

相關文章
相關標籤/搜索