為何jquery例項化的時候不需要new 一個建構函式

健兒發表於2018-04-01

如題,這個問題我曾經在支付寶的電話面試裡面最後一個問題被問到過,後來也沒有去看到底為何不需要new,現在我們就來剖析下。
而且當你在看jquery原始碼的時候,如果一開始就搞不懂這樣的問題,抑或jQuery.fn.init.prototype =jQuery.fn 這樣的問題也搞不懂的話,那基本後面的東西都是懵的。

首先回顧下,我們一般是如何寫外掛的,通常是這樣的

function Kissy () {
  
}

Kissy.prototype.alert = function () {
 
}
// 例項化必須要有,否則無法呼叫原型鏈上的alert方法
var kissy = new Kissy()
kissy.alert()
// 如果我們直接這樣呼叫
Kissy().alert()
// 就會報錯alert不是一個function。這裡涉及到原型繼承,new 一個建構函式和 執行普通函式的區別,不在贅述。

迴歸一個知識點,有助於我們理解後面我們講解的內容

建構函式有return值怎麼辦?
建構函式裡沒有顯式呼叫return時,預設是返回this物件,也就是新建立的例項物件。
當建構函式裡呼叫return時,分兩種情況:
1.return的是五種簡單資料型別:String,Number,Boolean,Null,Undefined。
這種情況下,忽視return值,依然返回this物件。
2.return的是Object
這種情況下,不再返回this物件,而是返回return語句的返回值。

如何改造上面的程式碼,可以不用new函式,直接呼叫到alert方法呢?很簡單。改造如下:

function Kissy () {
  return Kissy.prototype
}

當我們直接呼叫Kissy()函式的時候,不是返回的null或undefined而是返回Kissy的原型,即返回了一個原型物件,這個物件上是有alert方法的,這就好像我們熟悉的下面的程式碼一樣

var obj = {
  name: `zj`,
  getName: function() {
    console.log(this.name)
  }
}
obj.getName() // zj

接下來,我們仿造jquery原始碼又來改造下:

function Kissy () {
      return new Kissy.fn.init()
}
    
Kissy.fn = Kissy.prototype = {
  constructor: Kissy,
  init: function() {
      console.log(`init`)
  }
}

Kissy.fn.init.prototype = Kissy.fn

Kissy.fn.alert = function() {
    alert(`0000`)
}
Kissy().alert()  // 00000

無非就是在Kissy上面增加了一個fn屬性,這個屬性指向了Kissy.prototype。這樣的目的是便於我們開發外掛的時候,在原型上增加方法可以直接這麼寫Kissy.fn.alert 僅此而已。為何建構函式不直接返回
Kissy.fn呢 而是在中間搞了一個init.讀過jquery原始碼的都知道,jquery.fn.init 這個函式實際是jquery的初始化函式。這裡就不在展開了。

return new Kissy.fn.init()

建構函式返回了一個Kissy.fn.init()這個函式的一個例項。當然就會繼承這個例項上的原型,而原型又被我們重置了

Kissy.fn.init.prototype = Kissy.fn

所以就能呼叫到Kissy.fn上面的alert方法了,不信大家可以試試哦。

再來看jquery的原始碼,對比剛剛分析的程式碼,是不是基本一模一樣,只是jquery原始碼fn.init方法有比較多初始化的內容。

jQuery = function( selector, context ) {
    return new jQuery.fn.init( selector, context, rootjQuery );
},
jQuery.fn = jQuery.prototype = { //fn即對應prototype
    constructor: jQuery,
    init: function( selector, context, rootjQuery ) {
        ...
        return this;
    }
    ...
}
jQuery.fn.init.prototype = jQuery.fn;

相關文章