寫 Javascript 一直都不是我的強項,所以當 jQuery 一推出的時候,簡直是驚為天人!這麼好用的東西為什麼現在才會出現呢?那我在 1998 年寫得那些,不就是跟垃圾一樣了嘛(眼神死
所以回過頭來,研究一下 Plugin 也是很合理的。
Plugin 該怎麼開始
通常我們看到的範例會這樣寫,
(function($) {
var defaults = {};
$.fn.myplugin = function() {
return $(this).each(function() {
});
};
})(jQuery);
然後呢,我們就在 return...
的區塊裡面,開始寫我們要用的東西。這樣的寫法有個缺點,就是 this
的問題會讓你很頭痛。另外,當我們想要對這個 Plugin 作一些擴充動作的時候,這樣也會衍生出一些不必要得麻煩。
那我們還可以怎麼做?
(function($) {
var defaults = {};
var methods = {
myMethodOne: function my_method_one() {},
myMethodTwo: function my_method_two() {}
};
$.fn.myplugin = function() {
return $(this).each(function() {
});
};
})(jQuery);
另外一種思考的方式是,我們可以定義一個叫做 methods
的物件,然後把我們所需要的 方法
都打包起來,同樣的,你還是會遇到 this
的問題。但是,相較之下,這樣的作法對於往後的擴充性來說,多少會有點幫助。
那還有其他的方式嗎?
(function($) {
$.myplugin = function myplugin() {
};
$.myplugin.prototype = {
};
$.fn.myplugin = function() {
};
})(jQuery);
這是一種利用 Javascript 的原生 prototype 的方式來打包,好處是,擴充性足,而且也可以利用一些巧妙的方法,避開 this
衍生的問題。當然,這些方法並不是非常完美的解法,但是已敷使用。
什麼是 this 的問題
其實牽扯到 this
總是會有講不完的東西,要詳細解釋實在有點惱人。最簡單,淺顯易懂的例子,大概就是,
$('#container').each(function() {
var instance = this; // 這裡的 this 意指 $('#container')
var myObj = new myObject();
myObj.mySimpleFunc(function() {
var instance = this; // 這裡的 this 是 myObj
});
$(this).children().eq(0).bind('click', function(event) {
var instance = this; // 這裡的 this 是 $('#container').children().eq(0)
}, false);
});
雖然他是很奇妙又好用的東西,但是東西一旦多起來,就會讓人覺得又愛又恨了。所以說,jQuery 雖然有個叫做 $.proxy
的東西,可以用來避免這些事情,但是,其實我從頭到尾都沒仔細去弄懂,$.proxy
他到底在幹麼(眼神死
包裝你的 Plugin
形式有很多,但是我這裡想說的是 prototype 這個方法。原因很簡單,第一,擴充性強,第二,我可以避開 this
的問題。光就這兩點來說,我就可以省去很多麻煩了。
所以,我們一樣從頭開始,
(function(window, $, undefined) {
$.myplugin = function myplugin(options, callback, element) {
this.element = $(element);
this._init(options, callback);
};
$.myplugin.defaults = {
page: 1
};
$.myplugin.prototype = {
_init: function myplugin_init(options, callback) {
var instance = this, opts = this.options = $.extend(true, {}, $.myplugin.defaults, options);
// 開始做事
// 最後回呼,如果你不要就把 callback 拿掉就好
if($.isFunction(callback)) callback(this);
},
/*
* 這是一個公開方法的範例,用於更新 options 設定值
* 使用方式是 $('main').myplugin('update', {page: 3});
* 要留意的是,呼叫方法並不需要回呼的動作
* 當然你要也可以,只是看起來很假會
*/
update: function myplugin_update(options) {
var options = options || {};
if ($.isPlainObject(options)) {
this.options = $.extend(true, {}, this.options, options);
}
}
};
$.fn.myplugin = function(options, callback) {
var thisCall = typeof options;
switch(thisCall) {
// method
case 'string':
var args = Array.prototype.slice.call(arguments, 1);
this.each(function() {
var instance = $.data(this, 'myplugin');
if (!instance) {
return false;
}
// 我們使用 _ 開頭的函式來當作私有函式,所以不允許由外部呼叫
if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {
return false;
}
instance[options].apply(instance, args);
});
break;
// creation
case 'object':
this.each(function() {
var instance = $.data(this, 'myplugin');
if (instance) {
instance.update(options);
} else {
// 我們透過 new 來動態建立一個我們所寫好的 prototype
// 並且將他利用 $.data 的方式儲存起來
// 好處是,我們隨時都可以用 $.data 把他拿出來作壞事
$.data(this, 'myplugin', new $.myplugin(options, callback, this));
}
});
break;
}
return this;
};
})(window, jQuery);
這個看似複雜的包裝方法,其實實作起來並沒有想像中的困難。而且我已經順利的將之前寫過的 Plugin,以這種方式重新打包,運作也相當順利。只是,我不知道這種方式,和原先 jQuery 的傳統方法,哪一個效能會比較好,抑或是哪一個會比較不吃瀏覽器記憶體。
我不知道該怎麼去檢測這些事情,所以就隨他去吧(不負責任貌
結語
沒事不要學人家寫 Plugin