如題,這個問題我曾經在支付寶的電話面試裏面最後一個問題被問到過,後來也沒有去看到底爲什麼不須要new,如今咱們就來剖析下。
並且當你在看jquery源碼的時候,若是一開始就搞不懂這樣的問題,抑或jQuery.fn.init.prototype =jQuery.fn 這樣的問題也搞不懂的話,那基本後面的東西都是懵的。jquery
首先回顧下,咱們通常是如何寫插件的,一般是這樣的面試
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語句的返回值。this
如何改造上面的代碼,能夠不用new函數,直接調用到alert方法呢?很簡單。改造以下:prototype
function Kissy () { return Kissy.prototype }
當咱們直接調用Kissy()函數的時候,不是返回的null或undefined而是返回Kissy的原型,即返回了一個原型對象,這個對象上是有alert方法的,這就好像咱們熟悉的下面的代碼同樣插件
var obj = { name: 'zj', getName: function() { console.log(this.name) } } obj.getName() // zj
接下來,咱們仿造jquery源碼又來改造下:code
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;