如何開發jQuery自定義插件二

   jQuery本身功能強大,又提供了超強的可擴展性,便於人們爲其開發各種插件,本文便講解如何開發jQuery自定義插件。有幸遇到下面這篇前端大牛的神作,遂本文僅參照此文略作修改,再次感謝這位前端大牛的神作。

文檔來源:http://www.cnblogs.com/Wayou/p/jquery_plugin_tutorial.html

本文jQuery插件所操作的html代碼如下:

<nav>
    <ul id="nav">
        <li><a href="http://www.baidu.com" title="首頁" target="_blank">首頁</a></li>
        <li><a href="http://www.sina.com" title="新聞" target="_blank">新聞</a></li>
        <li><a href="http://www.qq.com" title="評論" target="_blank">評論</a></li>
    </ul>
</nav>

本文將要用到的插件調用方式如下:

<script type="text/javascript" src="jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="jquery.change.min.js"></script>
<script type="text/javascript">
    $(function(){
        $("#nav").find('li').change({
            "border":"1px solid blue",
            "fontSize":"16px"
        })
    })
</script>

如何開發jQuery自定義插件一 中,講到第二種開發插件方式的第5步《保護默認參數》,本文繼續後續章節

3.6 面向對象的插件開發

再進一步,面向對象的插件開發
到此,一個簡單的插件已經開發完成
但如要編寫一個複雜的插件,代碼量會很大,如何組織代碼就成了一個需要面臨的問題,沒有一個好的方式來組織這些代碼,整體感覺會雜亂無章,同時也不好維護,所以將插件的所有方法屬性包裝到一個對象上,用面向對象的思維來進行開發,無疑會使工作輕鬆很多。

如果沒有面向對象的思維,就會是需要一個方法的時候就去定義一個function,當需要另外一個方法的時候,再去隨便定義一個function,同樣,需要一個變量的時候,毫無規則地定義一些散落在代碼各處的變量。這樣不方便維護,代碼結構也不夠清晰

所以正確的方法是:將需要的重要變量定義到對象的屬性上,函數變成對象的方法,當我們需要的時候通過對象來獲取,這樣做的好處:
一來方便管理,
二來不會影響外部命名空間,因爲所有這些變量名還有方法名都是在對象內部。

接着上面的例子,我們可以把這個插件抽象成一個美化頁面的對象,它的功能是設置顏色、字體,還可以加入其他功能比如設置下劃線等。對於這個例子抽象成對象有點小題大做,這裏只是借這個例子說明如果開發面向對象的插件。

//新建一個對象命名爲Beautifier,然後在插件裏使用這個對象來編碼。
//定義Beautifier的構造函數
var Beautifier = function(ele,opt){
        //ele接收插件中的this實參,爲jQuery選中的元素集合
        //本Beautifier對象內部的this是DOM對象,不是jQuery類型
        this.$element = ele,
        //默認參數
        this.defaults = {
            'border':'1px solid red',
            'fontSize':'14px',
            "marginBottom":"5px",
            "color":"#f00"
        }
        //合併參數
        this.options = $.extend({},this.defaults,opt);

        //以後在此處添加新的變量即可,格式爲:
        //this.變量名 = 變量值
    }
//定義Beautifier的方法
Beautifier.prototype = {
        beautify:function(){
            return this.$element.css({
                 'border':this.options.border,
                 'fontSize':this.options.fontSize,
                 "marginBottom":this.options.marginBottom,
                 "color":this.options.color
             });
        }
        //以後在此處添加新的方法即可,格式爲:
        //,方法名:function(){
        //      return this.$element.jQuery方法()
        //}
    }
//在插件中使用Beautifier對象
$.fn.change = function(options){
        //創建Beautifier的實體
        //this爲jQuery選中的元素集合
        var beautifier = new Beautifier(this,options);
        //調用其beautify方法
        return beautifier.beautify();
        //以後在此處調用新的方法即可,格式爲:
        //return beautifier.方法名()
}    

通過上面這樣一改造,代碼變得更面向對象了,也更好維護和理解,以後要加新功能新方法,只需向對象添加新變量及方法即可,然後在插件裏實例化後即可調用新添加的東西。
插件的調用還是一樣的,對代碼的改動並不影響插件其他地方,只是將代碼的組織結構改動了而以。
到這裏,就可以更好地編寫複雜的插件同時很好地組織代碼了

3.7 創建獨立的命名空間,即用自調用匿名函數包裹你的代碼

再進一步,創建獨立的命名空間,即用自調用匿名函數包裹你的代碼

不僅僅是jQuery插件的開發,我們在寫任何JS代碼時都應該注意的一點是不要污染全局命名空間。因爲隨着你代碼的增多,如果有意無意在全局範圍內定義一些變量的話,最後很難維護,也容易跟別人寫的代碼有衝突。

比如在代碼中向全局window對象添加了一個變量status用於存放狀態,
同時頁面中引用了另一個別人寫的庫,他也向全局添加了這樣一個同名變量,最後的結果肯定不是你想要的。所以不到萬不得已,一般我們不會將變量定義成全局的。但有時全局變量又是非加不可的

JavaScript中無法用花括號方便地創建作用域,但函數卻可以形成一個作用域,域內的代碼是無法被外界訪問的。如果我們將自己的代碼放入一個函數中,那麼就不會污染全局命名空間,同時不會和別的代碼衝突。

如上面定義了一個Beautifier全局變量,它會被附到全局的window對象上,爲了防止這種事情發生,你或許會說,把所有代碼放到jQuery的插件定義代碼裏面去啊,也就是放到$.fn.myPlugin裏面。
這樣做倒也是種選擇。但會讓我們實際跟插件定義有關的代碼變得臃腫,
而在$.fn.myPlugin裏面我們其實應該更專注於插件的調用,以及如何與jQuery互動。

好的做法是始終用自調用匿名函數包裹你的代碼,這樣就可以完全放心,安全地將它用於任何地方了,絕對沒有衝突

自調用匿名函數的結構:

 (function(形參){
     //代碼部分
 })(實參)

所以保持上面例子的代碼不變,將所有代碼用自調用匿名函數包裹即可。
這樣做還有一個好處就是,自調用匿名函數裏面的代碼會在第一時間執行,頁面準備好過後,上面的代碼就將插件準備好了,以方便在後面的代碼中使用插件。
到目前爲止插件的代碼接近完美了。但還是要再考慮到其他一些因素

3.8 自調用匿名函數前添加分號

如果將自調用匿名函數這段代碼放到頁面最後,而其前面是別人寫的代碼沒有用分號結尾,那結果將是不可預測的。這是因爲用來充當自調用匿名函數的第一對括號與上面別人定義的函數相連(因爲中間沒有分號)。這將導致我們的代碼無法正常解析,所以報錯。

好的做法是在自調用匿名函數前加一個分號,這在任何時候都是一個好的習慣。

代碼如下:

;(function(){//注意這裏的分號必須加
     //所有插件的代碼
})()

3.9 將系統變量以變量形式傳遞到插件內部

如果我們插件代碼之前的代碼裏有將window, undefined等這些系統變量或者關鍵字修改掉了,正好我們又在自己的插件代碼裏面進行了使用,那結果也是不可預測的

好的做法是將系統變量以參數形式傳遞到插件內部

當這樣做之後,window等系統變量在插件內部就有了一個局部的引用,可以提高訪問速度,性能也可能會有提升

最終,我們得到一個非常安全、結構良好的代碼框架如下:

;(function($,window,document,undefined){//注意這裏的分號必須加
    //插件的全部代碼
})(jQuery,window,document);

至於其中的undefined,稍微有意思一點,爲了得到沒有被修改的undefined,我們並沒有傳遞這個參數,但卻在接收時接收了它,因爲實際並沒有傳,所以‘undefined’那個位置接收到的就是真實的’undefined’了。

所以最終的插件代碼如下:

;(function($,window,document,undefined){//注意這裏的分號必須加
    //插件的全部代碼

    //定義Beautifier的構造函數
    var Beautifier = function(ele,opt){
        //ele接收插件中的this實參,爲jQuery選中的元素集合
        //本Beautifier對象內部的this是DOM對象,不是jQuery類型
        this.$element = ele,
        //默認參數
        this.defaults = {
            'border':'1px solid red',
            'fontSize':'14px',
            "marginBottom":"5px",
            "color":"#f00"
        }
        //合併參數
        this.options = $.extend({},this.defaults,opt);

        //以後在此處添加新的變量即可,格式爲:
        //this.變量名 = 變量值
    }

    //定義Beautifier的方法
    Beautifier.prototype = {
        beautify:function(){
            // 此處的this是調用該方法的Beautifier對象的實例
            // 因this在不同的方法裏,指向的對象不同,所以此處保存this引用到self變量中
            self = this;
            return this.$element.each(function(){
                //each方法裏的的this是this.$element集合裏的每一個具體元素的DOM類型
                //所以需要將this進行jQuery包裝(即$(this))後,才能調用jQuery的方法
                $(this).css({
                    'border':self.options.border,
                    'fontSize':self.options.fontSize,
                    "marginBottom":self.options.marginBottom,
                    "color":self.options.color
                });
                $(this).append('   ' + $(this).find("a").attr('href'));
            })
        }
        //以後在此處添加新的方法即可,格式爲:
        //,方法名:function(){
        //      return this.$element.jQuery方法()
        //}
    }
    //在插件中使用Beautifier對象
    $.fn.change = function(options){
            //創建Beautifier的實體
            //this爲jQuery選中的元素集合
            var beautifier = new Beautifier(this,options);
            //調用其beautify方法
            return beautifier.beautify();
            //以後在此處調用新的方法即可,格式爲:
            //return beautifier.方法名()
    }
})(jQuery,window,document);

一個命名空間安全、結構良好、面向對象、組織有序的插件編寫完成。

附 關於變量定義及命名

現在談談關於變量及方法等的命名,沒有硬性規定,但爲了規範,遵循一些約定還是很有必要的。

變量定義:好的做法是把將要使用的所有變量名用一個var關鍵字一起定義在代碼開頭,變量名間用逗號隔開。

原因有二:
一是便於理解,知道下面的代碼會用到哪些變量,同時代碼顯得整潔且有規律,也方便管理,變量定義與邏輯代碼分開;
二是因爲JavaScript中所有變量及函數名會自動提升,也稱之爲JavaScript的Hoist特性,即使你將變量的定義穿插在邏輯代碼中,在代碼解析運行期間,這些變量的聲明還是被提升到了當前作用域最頂端的,
所以我們將變量定義在一個作用域的開頭是更符合邏輯的一種做法。當然,再次說明這只是一種約定,不是必需的。

變量及函數命名: 一般使用駝峯命名法(CamelCase),即首個單詞的首字母小寫,後面單詞首字母大寫

比如resultArray,requestAnimationFrame。

常量命名: 所有字母採用大寫,多個單詞用下劃線隔開,

比如WIDTH=100,BRUSH_COLOR=’#00ff00’。

當變量是jQuery類型時,建議以$開頭,因爲這可以很方便地將它與普通變量區別開來

這樣一看到以$開頭就知道它是jQuery類型可以直接在其身上調用jQuery相關的方法,比如var $element=$(‘a’);
之後就可以在後面的代碼中很方便地使用$element.css()、$element.first()等等,並且與普通變量容易區分開來。

引號的使用:一般HTML代碼裏面使用雙引號,而在JavaScript中多用單引號,
如下面代碼所示:

var name = 'Wayou';
document.getElementById(‘example’).innerHTML = '< a href="http: wayouliu.duapp.com/">'+name+'</a>'; 
//href=".." HTML中保持雙引號,JavaScript中保持單引號

這樣的好處:
一方面,HTML代碼中本來就使用的是雙引號,
另一方面,在JavaScript中引號中還需要引號的時候,要求我們單雙引號間隔着寫纔是合法的語句,除非你使用轉意符那也是可以的。
再者,堅持這樣的統一可以保持代碼風格的一致,不會出現這裏的字符串用雙引號包着,另外的地方在用單引號。

附 代碼混淆與壓縮

進行完上面的步驟,已經小有所成了。或許你很早就注意到了,你下載的插件裏面,一般都會提供一個壓縮的版本一般在文件名裏帶個’min’字樣。也就是minified的意思,壓縮濃縮後的版本。並且平時我們使用的jQuery也是官網提供的壓縮版本,jquery.min.js。

這裏的壓縮不是指代碼進行功能上的壓縮,而是通過將代碼裏面的變量名,方法函數名等等用更短的名稱來替換,並且刪除註釋(如果有的話)刪除代碼間的空白及換行所得到的濃縮版本。同時由於代碼裏面的各種名稱都已經被替代,別人無法閱讀和分清其邏輯,也起到了混淆代碼的作用。

壓縮的好處

源碼經過混淆壓縮後,體積大大減小,使代碼變得輕量級,同時加快了下載速度,頁面加載變快。比如正常jQuery v1.11.0的源碼是276kb,而壓縮後的版本僅94.1kb!體積減小一半還多。這個體積的減小對於文件下載速度的提升不可小覷。
經過壓縮混淆後,代碼還能閱讀嘛?當然不能,所以順帶還起到了代碼保護的作用。當然只是針對你編寫了一些比較酷的代碼又不想別人抄襲的情況。對於jQuery社區,這裏本身就是開源的世界,同時JavaScript這東西其實也沒什麼實質性方法可以防止別人查看閱讀你的代碼,畢竟有混淆就有反混淆工具,這裏代碼壓縮更多的還是上面提到的壓縮文件的作用,同時一定程度上防止別人抄襲。

工具

所使用的工具推崇的是Google開發的Closure Compiler。該工具需要Java環境的支持,所以使用前你可能需要先在機子上裝JRE, 然後再獲取Closure進行使用。

同時也有很朋在線的代碼混淆壓縮工具,用起來也很方便。這些工具都是一搜一大把的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章