對於大部分前端開發者而言,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上的方法?繼承之間的關係又是怎麼樣的?若是你對這些有興趣,能夠閱讀博主這篇博客:
ES6新增的class類怎麼用?與傳統構造函數寫法有哪些區別?如何快速上手class類?那你能夠閱讀博主這篇博客:
最後模擬實現new方法中,...rest是個什麼參數?若是你對這個存疑,那怕是得了解下ES6中的取代arguments的rest參數,歡迎閱讀這篇:
原諒個人厚顏無恥,若是你以爲本文對你有所幫助,歡迎評論,點贊與關注。