jQuery插件寫(xiě)法總結(jié)以及面向?qū)ο蠓绞綄?xiě)法總結(jié)

前言

最近在折騰jQuery插件,寫(xiě)成插件的目的就是為了實(shí)現(xiàn)功能與項(xiàng)目相分離,使得這個(gè)代碼在下一個(gè)項(xiàng)目中能直接引用不出錯(cuò)馋袜。這使得我們?cè)趯?xiě)插件的時(shí)候黔攒,就得考慮清楚趁啸,怎么寫(xiě)才能使得插件能夠通用强缘、靈活度高、可配置不傅、兼容性好旅掂、易用性高、耦合度低等访娶。
接下來(lái)就對(duì)以下幾種寫(xiě)法進(jìn)行分析商虐,前兩個(gè)是jQuery插件,后面2個(gè)是以對(duì)象的形式開(kāi)發(fā)震肮,都類(lèi)似称龙。而且寫(xiě)法也很多,我們要懂得這樣寫(xiě)的利弊戳晌。另一篇基礎(chǔ)文章:jQuery 插件寫(xiě)法
寫(xiě)法一
插件主體

(function($, window){
    // 初始態(tài)定義
    var _oDialogCollections = {};
 
    // 插件定義
    $.fn.MNDialog = function (_aoConfig) {
        // 默認(rèn)參數(shù)鲫尊,可被重寫(xiě)
        var defaults = {
            // string
            sId : "",
            // num
            nWidth : 400,
            // bollean
            bDisplayHeader : true,
            // object
            oContentHtml : "",
            // function
            fCloseCallback : null
        };
 
        var _oSelf = this,
            $this = $(this);
 
        // 插件配置
        this.oConfig = $.extend(defaults, _aoConfig);
 
        // 初始化函數(shù)
        var _init = function () {
            if (_oDialogCollections) {
                // 對(duì)于已初始化的處理
                // 如果此時(shí)已經(jīng)存在彈框,則remove掉再添加新的彈框
            }
            // 初始化彈出框數(shù)據(jù)
            _initData();
            // 事件綁定
            _loadEvent();
            // 加載內(nèi)容
            _loadContent();            
        }
        // 私有函數(shù)
        var _initData = function () {};
        var _loadEvent = function () {};
        var _loadContent = function () {
            // 內(nèi)容(分字符和函數(shù)兩種沦偎,字符為靜態(tài)模板疫向,函數(shù)為異步請(qǐng)求后組裝的模板,會(huì)延遲豪嚎,所以特殊處理)
            if($.isFunction(_oSelf.oConfig.oContentHtml)) {
                _oSelf.oConfig.oContentHtml.call(_oSelf, function(oCallbackHtml) {
                    // 便于傳帶參函數(shù)進(jìn)來(lái)并且執(zhí)行
                    _oSelf.html(oCallbackHtml);
                    // 有回調(diào)函數(shù)則執(zhí)行
                    _oSelf.oConfig.fLoadedCallback && _oSelf.oConfig.fLoadedCallback.call(_oSelf, _oSelf._oContainer$);
                });
            } else if ($.type(_oSelf.oConfig.oContentHtml) === "string") {
                _oSelf.html(_oSelf.oConfig.oContentHtml);
                _oSelf.oConfig.fLoadedCallback && _oSelf.oConfig.fLoadedCallback.call(_oSelf, _oSelf._oContainer$);
            } else {
                console.log("彈出框的內(nèi)容格式不對(duì)搔驼,應(yīng)為function或者string。");
            }
        };
 
        // 內(nèi)部使用參數(shù)
        var _oEventAlias = {
            click         : 'D_ck',
            dblclick     : 'D_dbl'
        };
 
        // 提供外部函數(shù)
        this.close = function () {
            _close();
        }        
 
        // 啟動(dòng)插件
        _init();
 
        // 鏈?zhǔn)秸{(diào)用
        return this;        
    };
    // 插件結(jié)束
})(jQuery, window);

調(diào)用


var MNDialog = $("#header").MNDialog({
    sId : "#footer",        //覆蓋默認(rèn)值
    fCloseCallback : dialog,//回調(diào)函數(shù)
    oContentHtml : function(_aoCallback){
            _aoCallback(_oEditGrpDlgView.el);
        }
    }
});
// 調(diào)用提供的函數(shù)
MNDialog.close;
function dialog(){
 
}

點(diǎn)評(píng)

1. 自調(diào)用匿名函數(shù)

(function($, window) {
  // jquery code
})(jQuery, window);

用處:通過(guò)定義一個(gè)匿名函數(shù)侈询,創(chuàng)建了一個(gè)“私有”的命名空間舌涨,該命名空間的變量和方法,不會(huì)破壞全局的命名空間扔字。這點(diǎn)非常有用也是一個(gè)JS框架必須支持的功能囊嘉,jQuery被應(yīng)用在成千上萬(wàn)的JavaScript程序中,必須確保jQuery創(chuàng)建的變量不能和導(dǎo)入他的程序所使用的變量發(fā)生沖突革为。

2. 匿名函數(shù)為什么要傳入window

通過(guò)傳入window變量扭粱,使得window由全局變量變?yōu)榫植孔兞浚?dāng)在jQuery代碼塊中訪問(wèn)window時(shí)震檩,不需要將作用域鏈回退到頂層作用域琢蛤,這樣可以更快的訪問(wèn)window;這還不是關(guān)鍵所在抛虏,更重要的是博其,將window作為參數(shù)傳入,可以在壓縮代碼時(shí)進(jìn)行優(yōu)化迂猴,看看jquery.min.js:

(function(a,b){})(jQuery, window); // jQuery被優(yōu)化為a, window 被優(yōu)化為 b

3. 全局變量this定義

var _oSelf = this,
$this = $(this);

使得在插件的函數(shù)內(nèi)可以使用指向插件的this
4. 插件配置

this.oConfig = $.extend(defaults, _aoConfig);

設(shè)置默認(rèn)參數(shù)贺奠,同時(shí)也可以再插件定義時(shí)傳入?yún)?shù)覆蓋默認(rèn)值
5. 初始化函數(shù)
一般的插件會(huì)有init初始化函數(shù)并在插件的尾部初始化
6. 私有函數(shù)、公有函數(shù)
私有函數(shù):插件內(nèi)使用错忱,函數(shù)名使用”_”作為前綴標(biāo)識(shí)
共有函數(shù):可在插件外使用儡率,函數(shù)名使用”this.”作為前綴標(biāo)識(shí)挂据,作為插件的一個(gè)方法供外部使用
7. return this
最后返回jQuery對(duì)象,便于jQuery的鏈?zhǔn)讲僮?br> 寫(xiě)法二
主體結(jié)構(gòu)

(function($){
    $.fn.addrInput = function(_aoOptions){
        var _oSelf = this;
        _oSelf.sVersion = 'version-1.0.0';
        _oSelf.oConfig = {
            nInputLimitNum : 9
        };
        // 插件配置
        $.extend(_oSelf.oConfig, _aoOptions);
 
        // 調(diào)用這個(gè)對(duì)象的方法儿普,傳遞this
        $.fn.addrInput._initUI.call(_oSelf, event);
        $.fn.addrInput._initEvents.call(_oSelf);
 
        // 提供外部函數(shù)
        this.close = function () {
            _close();
        }
 
        //返回jQuery對(duì)象崎逃,便于Jquery的鏈?zhǔn)讲僮?   
        return _oSelf;                   
    }
    $.fn.addrInput._initUI = function(event){
        var _oSelf = this,
            _oTarget = $(event.currentTarget);
    }
    $.fn.addrInput._initEvents = function(){}
})(window.jQuery);

點(diǎn)評(píng)
1. 美觀
插件的方法寫(xiě)在外部,并通過(guò)在插件主體傳遞this的方式調(diào)用
2. 定義插件版本號(hào)
不過(guò)在這里還是沒(méi)有用到
3. 關(guān)于call
這里的第一個(gè)參數(shù)為傳遞this眉孩,后面的均為參數(shù)
語(yǔ)法:

call([thisObj[,arg1[, arg2[, [,.argN]]]]])

定義:調(diào)用一個(gè)對(duì)象的一個(gè)方法个绍,以另一個(gè)對(duì)象替換當(dāng)前對(duì)象。
說(shuō)明:call 方法可以用來(lái)代替另一個(gè)對(duì)象調(diào)用一個(gè)方法浪汪。call 方法可將一個(gè)函數(shù)的對(duì)象上下文從初始的上下文改變?yōu)橛?thisObj 指定的新對(duì)象巴柿。如果沒(méi)有提供 thisObj 參數(shù),那么 Global 對(duì)象被用作 thisObj死遭。
4. 關(guān)于”this”
在插件的方法中广恢,可能有用到指向插件的this、和指向事件觸發(fā)的this呀潭,所以事件觸發(fā)的this用event來(lái)獲取:event.cuerrntTarget
event.currentTarget:指向事件所綁定的元素钉迷,按照事件冒泡的方式,向上找到元素
event.target:始終指向事件發(fā)生時(shí)的元素

如:
html代碼

<div id="wrapper"> 
    <a href="#" id="inner">click here!</a> 
</div>

** js代碼**

$('#wrapper').click(function(e) { 
    console.log('#wrapper'); 
    console.log(e.currentTarget); 
    console.log(e.target); 
}); 
$('#inner').click(function(e) { 
    console.log('#inner'); 
    console.log(e.currentTarget); 
    console.log(e.target); 
});

結(jié)果輸出

#inner
<a href=?"#" id=?"inner">?click here!?</a>?
<a href=?"#" id=?"inner">?click here!?</a>?
#wrapper
<div id=?"wrapper">?<a href=?"#" id=?"inner">?click here!?</a>?</div>?
<a href=?"#" id=?"inner">?click here!?</a>?

寫(xiě)法三(原生寫(xiě)法)
主體結(jié)構(gòu)

var MemberCard = function(_aoOption){
    // 配置(默認(rèn)是從外面?zhèn)鬟M(jìn)來(lái))
    _aoOption || (_aoOption = {}) ;
    // 初始化函數(shù)
    _init(this);
}
 
var _init = function(_aoSelf) {
    // 函數(shù)執(zhí)行
    _initData(_aoSelf);
    // 調(diào)用對(duì)象的私有方法
    _aoSelf._timedHide();
}
 
var _initData = function ( _aoSelf ) {}
 
// 私有方法
MemberCard.prototype._timedHide = function(_aoOptions) {
    var _oSelf = this;
    clearTimeout(this.iHideTimer);    
    // 使用underscore.js的extend方法來(lái)實(shí)現(xiàn)屬性覆蓋
    var oDefault = extend( { nHideTime: 300 }, _aoOptions );
    _oSelf.iHideTimer = setTimeout( function(){
        // 調(diào)用對(duì)象的共有方法
        _oSelf.hide();
    }, oDefault.nHideTime);        
}
 
// 公有方法
MemberCard.prototype.hide = function(_aoOptions) {}

使用

var oColleagueCard = new MemberCard({ nHideTime: 200 });
oColleagueCard.hide();

點(diǎn)評(píng)
1. 關(guān)于屬性覆蓋(對(duì)象深拷貝)
原生函數(shù)實(shí)現(xiàn)方法

function getType(o){
    return ((_t = typeof(o)) == "object" ? o==null && "null" || Object.prototype.toString.call(o).slice(8,-1):_t).toLowerCase();
}
function extend(destination,source){
    for(var p in source){
        if(getType(source[p])=="array"||getType(source[p])=="object"){
            destination[p]=getType(source[p])=="array"?[]:{};
            arguments.callee(destination[p],source[p]);
        }else{
            destination[p]=source[p];
        }
    }
}

demo:

var test={a:"ss",b:[1,2,3],c:{d:"css",e:"cdd"}};
var test1={};
extend(test1,test);
test1.b[0]="change"; //改變test1的b屬性對(duì)象的第0個(gè)數(shù)組元素
alert(test.b[0]);  //不影響test钠署,返回1
alert(test1.b[0]); //返回change

基于jQuery的實(shí)現(xiàn)方法

jQuery.extend([deep], target, object1, [objectN]);

用一個(gè)或多個(gè)其他對(duì)象來(lái)擴(kuò)展一個(gè)對(duì)象糠聪,返回被擴(kuò)展的對(duì)象。
如果不指定target谐鼎,則給jQuery命名空間本身進(jìn)行擴(kuò)展舰蟆。這有助于插件作者為jQuery增加新方法。 如果第一個(gè)參數(shù)設(shè)置為true狸棍,則jQuery返回一個(gè)深層次的副本身害,遞歸地復(fù)制找到的任何對(duì)象。否則的話隔缀,副本會(huì)與原對(duì)象共享結(jié)構(gòu)。 未定義的屬性將不會(huì)被復(fù)制傍菇,然而從對(duì)象的原型繼承的屬性將會(huì)被復(fù)制猾瘸。
demo:

var options = {id: "nav", class: "header"}
var config = $.extend({id: "navi"}, options); //config={id: "nav", class: "header"}

** 2. 關(guān)于this**
這個(gè)對(duì)象的所有方法的this都指向這個(gè)對(duì)象,所以就不需要重新指定
寫(xiě)法四
主體結(jié)構(gòu)

function EditorUtil() {
    this._editorContent = $( '#editor_content' );
    this._picBtn = $( '#project_pic' );
    this.ieBookmark = null;
}
EditorUtil.prototype = {
    consturctor: EditorUtil,
 
    noteBookmark: function() {
    },
    htmlReplace: function( text ) {
        if( typeof text === 'string' ) {
            return text.replace( /[<>"&]/g, function( match, pos, originalText ) {
                switch( match ) {
                    case '<':
                        return '&lt;';
                    case '>':
                        return '&gt;';
                    case '&':
                        return '&amp;';
                    case '"':
                        return '&quot;';
                }
            });
        }
        return '';
    },
    init: function() {
        this._memBtn.bind( 'click', function( event ) {
            $(".error_content").hide();
            return false;
        });
    }
};
 
// 初始化富文本編輯器
var editor = new EditorUtil();
editor.init();

點(diǎn)評(píng)
寫(xiě)法四和寫(xiě)法三其實(shí)都差不多丢习,但是你們有沒(méi)有看出其中的不一樣呢牵触?
1. 兩種都是利用原型鏈給對(duì)象添加方法
寫(xiě)法三:

MemberCard.prototype._timedHide
MemberCard.prototype.hide

寫(xiě)法四:

EditorUtil.prototype = {
    consturctor: EditorUtil,
    noteBookmark: function(){},    
    htmlReplace: function(){}
}

細(xì)看寫(xiě)法四利用“對(duì)象直接量”的寫(xiě)法給EditorUtil對(duì)象添加方法,和寫(xiě)法三的區(qū)別在于寫(xiě)法四這樣寫(xiě)會(huì)造成consturctor屬性的改變
constructor屬性:始終指向創(chuàng)建當(dāng)前對(duì)象的構(gòu)造函數(shù)
每個(gè)函數(shù)都有一個(gè)默認(rèn)的屬性prototype咐低,而這個(gè)prototype的constructor默認(rèn)指向這個(gè)函數(shù)揽思。如下例所示:

function Person(name) {  
    this.name = name;  
};  
Person.prototype.getName = function() {  
    return this.name;  
};  
var p = new Person("ZhangSan");  
 
console.log(p.constructor === Person);  // true  
console.log(Person.prototype.constructor === Person); // true  
// 將上兩行代碼合并就得到如下結(jié)果  
console.log(p.constructor.prototype.constructor === Person); // true

當(dāng)時(shí)當(dāng)我們重新定義函數(shù)的prototype時(shí)(注意:和上例的區(qū)別,這里不是修改而是覆蓋)见擦,constructor屬性的行為就有點(diǎn)奇怪了钉汗,如下示例:

function Person(name) {  
    this.name = name;  
};  
Person.prototype = {  
    getName: function() {  
        return this.name;  
    }  
};  
var p = new Person("ZhangSan");  
console.log(p.constructor === Person);  // false  
console.log(Person.prototype.constructor === Person); // false  
console.log(p.constructor.prototype.constructor === Person); // false

為什么呢羹令?
原來(lái)是因?yàn)楦采wPerson.prototype時(shí),等價(jià)于進(jìn)行如下代碼操作:

Person.prototype = new Object({  
    getName: function() {  
        return this.name;  
    }  
});

而constructor屬性始終指向創(chuàng)建自身的構(gòu)造函數(shù)损痰,所以此時(shí)Person.prototype.constructor === Object福侈,即是:


function Person(name) {  
    this.name = name;  
};  
Person.prototype = {  
    getName: function() {  
        return this.name;  
    }  
};  
var p = new Person("ZhangSan");  
console.log(p.constructor === Object);  // true  
console.log(Person.prototype.constructor === Object); // true  
console.log(p.constructor.prototype.constructor === Object); // true

怎么修正這種問(wèn)題呢?方法也很簡(jiǎn)單卢未,重新覆蓋Person.prototype.constructor即可:

function Person(name) {  
    this.name = name;  
};  
Person.prototype = new Object({  
    getName: function() {  
        return this.name;  
    }  
});  
Person.prototype.constructor = Person;  
var p = new Person("ZhangSan");  
console.log(p.constructor === Person);  // true  
console.log(Person.prototype.constructor === Person); // true  
console.log(p.constructor.prototype.constructor === Person); // true
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肪凛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子辽社,更是在濱河造成了極大的恐慌伟墙,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滴铅,死亡現(xiàn)場(chǎng)離奇詭異戳葵,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)失息,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)譬淳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人盹兢,你說(shuō)我怎么就攤上這事邻梆。” “怎么了绎秒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵浦妄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我见芹,道長(zhǎng)剂娄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任玄呛,我火速辦了婚禮阅懦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘徘铝。我一直安慰自己耳胎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布惕它。 她就那樣靜靜地躺著怕午,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淹魄。 梳的紋絲不亂的頭發(fā)上郁惜,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音甲锡,去河邊找鬼兆蕉。 笑死羽戒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的恨樟。 我是一名探鬼主播半醉,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼劝术!你這毒婦竟也來(lái)了缩多?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤养晋,失蹤者是張志新(化名)和其女友劉穎衬吆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體绳泉,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逊抡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了零酪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冒嫡。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖四苇,靈堂內(nèi)的尸體忽然破棺而出孝凌,到底是詐尸還是另有隱情,我是刑警寧澤月腋,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布蟀架,位于F島的核電站,受9級(jí)特大地震影響榆骚,放射性物質(zhì)發(fā)生泄漏片拍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一妓肢、第九天 我趴在偏房一處隱蔽的房頂上張望捌省。 院中可真熱鬧,春花似錦碉钠、人聲如沸纲缓。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)色徘。三九已至恭金,卻和暖如春操禀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背横腿。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工颓屑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斤寂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓揪惦,卻偏偏與公主長(zhǎng)得像遍搞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子器腋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容