天天看點

jquery 插件 分析

很多人覺得jquery、ext等一些開源js源代碼 十分的晦澀,讀不懂,遇到問題需要調試也很費勁。其實我個人感覺主要是有幾個方面的原因:

1、對一些js不常用的文法、操作符不熟悉

2、某個function中又嵌套了一些内部的function,使得整個代碼的層次結構不像java代碼那麼清晰。

3、js中允許變量先使用後定義,會造成我們看代碼時候忽然冒出來一個變量、function,卻找不到是在哪裡定義的。

那麼今天給大家分享一下我的經驗,掃清你的障礙。

一、一些晦澀的操作符:

1、(function(){})();

 幾乎所有的開源js代碼開篇都是這樣(function(……){……})(……);

 下面是Jquery的部分源碼:

Js代碼  

(function( window, undefined ) {  

    var jQuery = function( selector, context ) {  

        // The jQuery object is actually just the init constructor 'enhanced'  

        return new jQuery.fn.init( selector, context );  

    },  

    // Map over jQuery in case of overwrite  

    _jQuery = window.jQuery,  

    // Map over the $ in case of overwrite  

    _$ = window.$,  

    ……  

    indexOf = Array.prototype.indexOf;  

    // Expose jQuery to the global object  

    window.jQuery = window.$ = jQuery;  

})(window);  

那麼這個操作符(function(){})();到底是什麼意思呢?

(function(){})中的定義了一個function,緊接着的()表示立即執行這個function。

我們看到Jquery源碼第一個()中是定義了一個匿名function( window, undefined ) {};接着末尾有個(window),就表示執行這個匿名function,并傳入參數window 。

在匿名function( window, undefined ) {}中,定義了一個局部變量jQuery;然後在末尾我們看到Jquery末尾有一句window.jQuery = window.$ = jQuery; 這句代碼就表示,将此前定義的jQuery導出到window對象 。這也是為什麼我們可以在代碼任何地方直接使用$、jQuery對象,因為在這裡已經将$、jQuery對象挂載到window下去了,而window.$、window.jQuery與直接使用$、jQuery是沒有差別的。

(注意,這個window對象是傳入的參數window,而不是浏覽器window對象!!一個形參、一個實參。我們可以在定義function的時候,将參數window取名為其他字元。是以我們看到jquery.min.js中這個匿名function變成了(function(E,B){})(window);)

通常(function(){})()用來封裝一些私有成員或者公共成員的導出。

2、令人迷惑的","

 我們知道“,”一般用于一次定義多個變量、定義多個參數等。像上面的jQuery源碼中在var jQuery後面,使用“,”一次定義了很多個變量。

 但是,像下面的代碼,可能大家就不一定看得懂了:

//html:<input type="hidden" value="king" id="nameHide"/>  

jQuery(document).ready(function() {  

    var showName=function(){  

        var value,nameInput=$("#nameHide");  

        return nameInput.show(),value=nameInput.val();  

    };  

    alert(showName());  

});  

//結果:彈出king  

這裡的“nameInput.show(),value=nameInput.val()”中的“,”運算符的作用是傳回","右側表達式的值。是以,return 後面如果有多個表達式,且表達式之間由","隔開,整個return表達式傳回的是最後一個","右側的表達式的值。

“,”在開源代碼中常常被用于return表達式中,以及跟下面我們要講到的"()"運算符一起使用。

3、“()”廣義上的代碼包裝

我們遇到複雜的邏輯表達式時,我們通常會把需要一起運算的表達式用“()”包起來:(a||b)&&(c||d)

其實,我們可以這樣了解:"()"運算符将一個表達式包裹起來作為一個整體進行運算,然後傳回這個整體的值。

 那麼上面的(function(){})()中左側定義function的()也是這個作用,将這個function給包裹起來,然後傳回這個function。我們調用方法一般是a();那麼(function(){})的作用就是傳回這個function對象,然後(function(){})()右側的()表示調用這個function。

 我們再來看其他的用法:

//html:<input value="kings" id="name"/><div id="nameErrorTip">輸入錯誤!</div>  

    var nameValidate=function(){  

        var value,nameInput=$("#name"),nameErrorTip=$("#nameErrorTip");   

        return (value=nameInput.val(),value=="king")?(nameErrorTip.hide(),"對了,輸入為king!"):(nameErrorTip.show(),"請輸入king!");  

    alert(nameValidate());  

//結果 nameErrorTip顯示,彈出"請輸入king!"  

//html:<input value="king" id="name"/><div id="nameErrorTip">輸入錯誤!</div>  

//結果 nameErrorTip隐藏,彈出"對了,輸入為king!"  

這裡“ (value=nameInput.val(),value=="king")”中"()"将裡面的表達式作為一個整體進行運算,而裡面的表達式又是由","構成的多個表達式組,是以執行的時候會把這多個表達式都執行一次 ,并且傳回最後一個表達式的值!

 是以 (value=nameInput.val(),value=="king")執行時,先運算value的值,再判斷是否為"king"。如果為king,會執行(nameErrorTip.hide(),"對了,輸入為king!")。這個表達式又先将nameErrorTip隐藏,再傳回一個"對了,輸入為king!"字元串作為 整個return的值。

4、||、&&、if()邏輯讓人頭暈

 ||、&&兩側參與運算的是邏輯表達式,if()中也是。但是我們在很多開源代碼中看到的||、&&參與運算的表達式看起來卻好像不是邏輯表達式……

 下面節選一段jQuery.tool中的一段源碼:

e.circular || (f.onBeforeSeek(function(a, b) {  

                    setTimeout(function() {  

                                a.isDefaultPrevented()  

                                        || (n.toggleClass(e.disabledClass,  

                                                b <= 0), o.toggleClass(  

                                                e.disabledClass, b >= f  

                                                        .getSize()  

                                                        - 1))  

                            }, 1)  

                }), e.initialIndex || n.addClass(e.disabledClass)), f.getSize() < 2  

                && n.add(o).addClass(e.disabledClass), e.mousewheel  

                && a.fn.mousewheel && b.mousewheel(function(a, b) {  

                            if (e.mousewheel) {  

                                f.move(b < 0 ? 1 : -1, e.wheelSpeed || 50);  

                                return !1  

                            }  

                        });  

這裡有多處||、&&。但與運算的表達式卻是調用某個函數的傳回值。

 其實,js中的邏輯表達式是按照真值、假值來分的。true是真值;1是真值;一個對象也是真值;false是假值;""、0是假值。

 在js中&&、||不一定都是用來判斷一個表達式的邏輯值是true、false,更多的是用來依據真值或者假值執行相應操作!

 我們知道,||運算的時候,會先運算左側的表達式的值,如果為真值,那麼真個表達式就為真值,而同時右側表達式是真值、假值都不重要,因為右側表達式都不再繼續參與運算了。又如果左側為假值,則繼續運算右側表達式。

 &&則先運算左側表達式,兩側表達式,一個為假值,則整個表達式為假值。

 這裡關鍵是這個真值或者假值的運算過程中,我們可以使用上面介紹的","、"()"将一組表達式串起來執行。也就是說,這個表達式可能會很長很長,我甚至可以定義一個function在裡面。這些表達式在執行過程中,有可以進行某些附加操作。比如我們希望這個表達式為真值的時候我們做什麼,假值的時候做什麼,把這些操作用"()"、","串起來作為一個整體運算。

 于是就有了上面的複雜代碼。

 我們來看個執行個體吧。是上面例子的更新版。我們加入一個nameInput是否存在的判斷:

        var value,nameInput=$("#name"),nameErrorTip=$("#nameErrorTip"),msg;  

        msg=(value=nameInput.val(),value=="king")?(nameErrorTip.hide(),"對了,輸入為king!"):(nameErrorTip.show(),"請輸入king!");  

        return (nameInput.length&&nameInput.val()&&nameErrorTip.length&&msg)||"沒有找到name輸入框或者輸入框沒有值!";  

測試:

//html:<input value="king" id="myName"/>  

//結果:彈出“沒有找到name輸入框或者輸入框沒有值!”  

//<input value="king" id="name"/><div id="nameErrorTip">輸入錯誤!</div>  

//結果:彈出“對了,輸入為king!”,nameErrorTip被隐藏  

return表示中 nameInput.length&&nameInput.val()&&nameErrorTip.length&&msg會先運算 nameInput.length的值,如果length為0則表達式為假值,如果為1則為真值。val()操作也是如此,如果val()結果為""則表達式也是假值。幾個表達式之間為&&運算,則表示依次運算幾個表達式的值,如果都未真值則傳回最後一個表達式的值 ,由于整個表達式與

 "沒有找到name輸入框或者輸入框沒有值!"

表達式之間是||運算,是以前面的表達式其中一個表達式為假值則傳回||右側的表達式的值,也就是整個“沒有找到name輸入框或者輸入框沒有值!”字元串。

談了這些難以了解的運算符後,大家可能會覺得,這個javascript為什麼要搞這些晦澀的運算符呢?

我的了解是因為javascript通常在用戶端運作,那麼從伺服器端将js代碼傳輸到用戶端肯定需要耗時。上面的這些運算符都是為了減少代碼量。再加上使用壓縮工具去掉空格,替換變量名,就可以使用壓縮率達到最好。