jQuery的無new建立方法

通常咱們去寫一個框架,會採用什麼樣的設計呢?好比設計一個jQuery框架,通常咱們會建立一個函數對象html

function jQuery(params){
  //構造函數
}
jQuery.prototype.init = function(params){
  //初始化操做
} jQuery.prototype.html
= function(html){   //實現相似設置innerHTML的操做 } var jq = new jQuery("#id"); jq.init(); jq.html("test");

我想這是咱們最多見的使用方式,這種方式須要new一個新的實例,而後再進行初始化操做,而後才能繼續操做,很顯然這樣會比較繁瑣並且代碼較多,而jQuery中的使用方法卻很是簡潔node

獲取指定元素:$("#id")/$(".class")/$("body").html("test");jquery

建立html片斷$("<div></div>").html("test");框架

設置文檔加載完成後的方法$(function(){XXX});函數

等等,爲什麼jQuery的使用方法如此簡單,這是由於jQuery設計的是一種無new的建立方法,這是jQuery獨特的設計思路之一。this

咱們打開jQuery初始化的這塊代碼,發現基本結構是這樣子的(提取主要框架部分源碼):spa

(function( window, undefined ) {
    
    var jQuery = function( selector, context ) {
        return new jQuery.fn.init( selector, context, rootjQuery );
    };

    jQuery.fn = jQuery.prototype = {

        constructor: jQuery,
        init: function( selector, context, rootjQuery ) {

                if ( !selector ) {
                     return this;
                 } prototype

                if ( typeof selector === "string" ) {
                    if ( match && (match[1] || !context) ) {
                        return this;
                    } else if ( !context || context.jquery ) {
                        return ( context || rootjQuery ).find( selector );
                    } else {
                        return this.constructor( context ).find( selector );
                    }
                } else if ( selector.nodeType ) {
                     return this;設計

                } else if ( jQuery.isFunction( selector ) ) {
                     return rootjQuery.ready( selector );
                }code

                if ( selector.selector !== undefined ) {
                     this.selector = selector.selector;
                     this.context = selector.context;
                 }

                 return jQuery.makeArray( selector, this );

        },
     html:function(value){
      //實現相似innerHTML操做
     } } jQuery.fn.init.prototype
= jQuery.fn;

})( window );

1. 首先把代碼封裝在一個匿名函數裏面,防止污染全局環境,而後建立一個對象jQuery,這個對象接受兩個參數selector,context,其中selector就是咱們一般傳入的 #id,.class等字符串,這個對象函數裏面並無作什麼邏輯而是直接返回了一個原型上的init方法,而後把參數傳了進去。

2. init方法接受了3個參數,而後在內部進行了各類判斷,根據不一樣的場景,返回一個不一樣的實例對象,相似一個工廠方法。

3. 將jQuery.fn.init的原型掛載到jquery的原型上。

4. 將jQuery的constructor還原爲jQuery

init方法的內部邏輯咱們後面再看,首先看下爲什麼jQuery要這樣作?

首先jQuery在構造函數裏面就執行了new方法並執行了init方法,將new和init兩步直接合併成一步,並且自帶new操做,不須要用戶再去進行new的操做,可是爲什麼要使用一個原型上的init方法,而不是在自身本身new出一個對象呢?

想象一下若是jQuery這樣寫

 var jQuery = function( selector, context ) {
     //作上面的各類場景判斷
return new jQuery( selector, context, rootjQuery ); }

這樣寫會存在一個問題,jQuery函數內部new一個新的jQuery,而後會一直這樣new下去無限循環,陷入死循環。爲了防止這種狀況的出行,jQuery將這部分邏輯放在了原型中的一個init方法,由init方法來進行工廠方法的處理,return new jQuery.fn.init( selector, context, rootjQuery ),這樣就避免了死循環的問題,並且實現了一步到位的初始化。

可是這樣寫還存在一個問題,init方法裏面的return this是指向的jQuery的實例對象,那他將沒法訪問jQuery的原型對象,好比說jQuery.prototype.html方法,由於html的方法是jQuery.prototype上的因此init方法沒法訪問這個函數,爲了解決這個問題,咱們看到以前的源碼有最後一行:

 jQuery.fn.init.prototype = jQuery.fn;

其中jQuery.fn就是jQuery.prototpye,也就等價於

jQuery.prototpye.init.prototype = jQuery.prototpye;

這句話的意思是,把init的方法的原型改成指向jQuery的原型,這樣new init的出來的實例對象也就等價於new jQuery的實例對象,因此也就能夠訪問jQuery的原型方法了。

jQuery.prototype.init和jQuery的原型指向的是同一個對象,這樣無論是init仍是jQuery去修改原型對象都是等價的,這樣就實現了上面提到的問題。

最後,jQuery再把constructor還願回來到jQuery,防止構造函數指向錯誤,引發調用問題,爲什麼要這樣處理呢?由於咱們是把jQuery的prototype直接覆蓋成了一個新的實例對象,因此jQuerty的constructor就變成了{}的構造函數Object,因此須要將constructor還原爲真正的jQuery。

對比下兩種原型改寫方式:

//方式1
jQuery.prototype.html = function(value){}
//方式2 jQuery.prototype
= { html:function(value){} }

兩種方式看起來好像沒啥區別,但其實區別很大,第一種是給jQuery的prototype新增一個方法,這種方式是不會影響jQuery的constructor,仍是function jQuery(),

而第二種方式是直接覆蓋了jQuery的prototype,這種狀況下的constructor會被直接改寫爲{html:function(value)}這個實例對象的構造函數,也就是function Object()

因此須要重置一下jQuery的constructor

相關文章
相關標籤/搜索