面試官真的會問:new的實現以及無new實例化

面試官很忙,但我不單純是蹭熱點,今天聊的主題絕對是面試中命中率很高的知識點。我在複習javascript函數這塊知識時,注意到一個有意思的點,就是構造函數顯式return,並由此引起了一波頭腦風暴......javascript

咱們知道,若是不作特殊處理,new構造函數時會發生下面這幾步。前端

  • 首先建立一個新對象,這個新對象的 __proto__屬性指向構造函數的 prototype屬性
  • 此時構造函數執行環境的 this指向這個新對象
  • 執行構造函數中的代碼,通常是經過 this給新對象添加新的成員屬性或方法。
  • 最後返回這個新對象。

下面咱們來驗證下:java

function Test() {
 console.log(JSON.stringify(this));  console.log(this.__proto__.constructor === Test);  this.name = 'jack';  this.age = 18;  console.log(JSON.stringify(this)); } var a = new Test(); // Chrome控制檯會輸出如下內容 // {} // true // {"name":"jack","age":18} 複製代碼

這徹底符合咱們的認知,沒毛病。web

實現一個new

那麼在認識到new實例化過程的幾個關鍵步驟後,咱們也能解答一道面試中常見的題目:如何實現一個new?面試

實現一個new也就意味着不能用new關鍵字,那麼要完成這麼一系列步驟,固然是經過函數實現了。app

// func是構造函數,...args是須要傳給構造函數的參數
function myNew(func, ...args) {  // 建立一個空對象,而且指定原型爲func.prototype  var obj = Object.create(func.prototype);  // new構造函數時要執行函數,同時指定this  func.call(obj, ...args);  // 最後return這個對象  return obj; } 複製代碼

以這四個關鍵步驟做爲指導思想,咱們很快就寫出了代碼實現。從這一點我也能體會到思路的重要性,別當工具人,代碼纔是工具!編輯器

從實現邏輯上來看沒什麼問題,咱們來驗證下。ide

function Test(name, age) {
 this.name = name;  this.age = age; }  myNew(Test, '小明', 18); // Chrome控制檯會輸出如下內容 // Test {name: "小明", age: 18} 複製代碼

構造函數顯式return

所謂顯式return,就是在構造函數中主動return一個對象,這裏說的對象不只包括Object,也包含ArrayDate等對象哦。函數

咱們能夠試一試:工具

function Test() {
 this.name = 'jack';  this.age = 18;  return {  content: '我有freestyle'  } } new Test(); // Chrome控制檯會輸出如下內容 // {content: "我有freestyle"} 複製代碼

那麼return一個普通類型數據有沒有用呢?好比字符串,數字?試試便知。

function Test() {
 this.name = 'jack';  this.age = 18;  return '我有freestyle' } new Test(); // Chrome控制檯會輸出如下內容 // Test {name: "jack", age: 18} 複製代碼

能夠看到,當咱們return一個普通類型數據時,不會影響結果,依然會返回new出來的這個新對象。

咱們也應該知道,new構造函數就是爲了建立對象,你return一個字符串之類的普通類型數據是沒有任何意義的,因此咱們的關注點應該是return一個特殊的對象。請接着往下看。

無new實例化

所謂「無new實例化」,就是指不經過new關鍵字實例化對象(固然,這裏說的不經過new,只是調用層面的,底層仍是用了new)。這一點咱們使用jQuery的時候已經體驗過了。

// 實例化了一個jQuery對象,可是沒有用到new
var ele = jQuery('<div>freestyle</div>'); 複製代碼

那麼這種黑科技是怎麼實現的呢?

前面已經提到了,咱們能夠在構造函數中經過顯式return來返回一個自定義的對象,那麼這裏就有發揮的空間了。咱們經過一個簡單的例子來感覺下:

function Shadow() {
 this.name = 'jack';  this.age = 18; }  function jQuery() {  return new Shadow(); }  var obj1 = jQuery(); console.log(obj1) // Chrome控制檯會輸出如下內容 // Shadow {name: "jack", age: 18} 複製代碼

jQuery()用了移花接木的障眼法完成了對象實例化,一手隱藏的new Shadow()讓咱們誤覺得不用new直接調用函數也能建立實例。

咱們再來試下new jQuery(),會發現,「臥槽,怎麼跟jQuery()執行結果如出一轍!」

var obj2 = new jQuery();
console.log(obj2) // Chrome控制檯會輸出如下內容 // Shadow {name: "jack", age: 18} 複製代碼

這是由於new構造函數顯式return了new Shadow(),這樣返回的結果也就是new Shadow()實例化出來的對象,而不使用new直接調用jQuery(),只是把jQuery()當成一個普通的函數執行,其結果不言而喻是new Shadow()實例化出來的對象。

因此,這裏new jQuery()jQuery()是等價的。

雖然jQuery已經用得愈來愈少,可是其設計思路很是值得咱們學習。那麼jQuery到底妙在哪裏?能夠說是不少,鏈式操做,插件體系這些特點都是咱們耳熟能詳的。不扯太多了,就讓咱們來簡單分析下jQuery實例化的過程。

我這裏拿到了jQuery v1.12.4版本的代碼,大概1W行,很舒服。

翻啊翻啊,翻到了第71行,看到了這麼一串代碼:

jQuery = function( selector, context ) {
 return new jQuery.fn.init( selector, context ); } 複製代碼

這不就是咱們熟悉的移花接木技術嗎?jQuery.fn.init彷佛就是上面例子中的Shadow。看着有點像了,可是仍是要好好研究下。

爲啥要搞個jQuery.fn?

jQuery.fn是jQuery.prototype的別名,是爲了代碼簡潔的考慮。這一點參考源碼第91行就能夠知道。

jQuery.fn = jQuery.prototype = {
// ...... 複製代碼

移花接木如何保證原型指向?

咱們知道,若是僅僅經過new jQuery.fn.init(selector, context)是存在一個問題的,問題就是獲得的實例不是jQuery的實例,而是jQuery.fn.init的實例。那麼如何處理這個問題呢?

咱們翻到源碼2866行,能夠看到:

init = jQuery.fn.init = function( selector, context, root ) {
 // 建立實例的具體邏輯 } 複製代碼

具體init方法怎麼建立一個jQuery對象,作了哪些判斷邏輯,這些都不是本文關注的重點。咱們須要關注的是,jQuery是如何保證明例化的對象的原型指向是正確的?否則實例化的對象如何使用jQuery.prototype上面掛載的諸多方法呢,好比this.show()this.hide()

緊接着翻到2982行,我有了答案:

init.prototype = jQuery.fn;
複製代碼
牛逼
牛逼

妙啊,這一手修改原型指向的操做,完美解決了這個問題。這樣一來,new init()獲得的實例天然也是jQuery的實例。

jQuery.prototype.init.prototype === jQuery.prototype; // true
var a = $('<div>123</div>') a instanceof jQuery // true a instanceof jQuery.fn.init // true 複製代碼

這樣一來,咱們能夠獲得一個基本的設計思路:

function myModule(params) {
 return new myModule.fn.init(params); } myModule.fn = myModule.prototype = {  constructor: myModule } myModule.fn.init = function(params) {  // 能夠對實例對象進行各類操做 } myModule.fn.init.prototype = myModule.prototype; 複製代碼

在這個基礎上,咱們能夠擴展靜態方法和原型方法,這個myModule模塊就變得愈來愈豐富。

最後

妙啊,一個構造函數,讓我陷入了思考......扶我起來,我還能學!

一塊兒學前端嗎
一塊兒學前端嗎
關注交流
關注交流

本文使用 mdnice 靈動藍主題 排版

相關文章
相關標籤/搜索