亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

老司機帶你解讀jQuery插件開發流程

2019-11-20 10:02:13
字體:
來源:轉載
供稿:網友

jquery插件開發模式

jquery插件一般有三種開發方式:

  • 通過$.extend()來擴展jQuery
  • 通過$.fn 向jQuery添加新的方法
  • 通過$.widget()應用jQuery UI的部件工廠方式創建

第一種$.extend()相對簡單,一般很少能夠獨立開發復雜插件,第三種是一種高級的開發模式,本文也不做介紹。第二種則是一般插件開發用到的方式,本文著重講講第二種。

插件開發

第二種插件開發方式一般是如下定義

$.fn.pluginName = function() {  //your code here}

插件開發,我們一般運用面向對象的思維方式

例如定義一個對象

var Haorooms= function(el, opt) {  this.$element = el,  this.defaults = {    'color': 'red',    'fontSize': '12px',    'textDecoration':'none'  },  this.options = $.extend({}, this.defaults, opt)}//定義haorooms的方法haorooms.prototype = {  changecss: function() {    return this.$element.css({      'color': this.options.color,      'fontSize': this.options.fontSize,      'textDecoration': this.options.textDecoration    });  }}

$.extend({}, this.defaults, opt)有{}主要是為了創建一個新對象,保留對象的默認值。

$.fn.myPlugin = function(options) {  //創建haorooms的實體  var haorooms= new Haorooms(this, options);  //調用其方法  return Haorooms.changecss();}

調用這個插件直接如下就可以

$(function() {  $('a').myPlugin({    'color': '#2C9929',    'fontSize': '20px'  });})

上述開發方法的問題

上面的開發方法存在一個嚴重的問題,就是定義了一個全局的Haorooms,這樣對于插件的兼容等等各個方面都不好。萬一別的地方用到了Haorooms,那么你的代碼就悲催了!現在我們把上面的代碼包裝起來,用一個自調用匿名函數(有時又叫塊級作用域或者私有作用域)包裹,就不會出現這個問題了!包括js插件的開發,也是一樣的,我們用一個自調用匿名函數把自己寫的代碼包裹起來,就可以了!包裹方法如下:

(function(){})()

用上面的這個包裹起來,就可以了。

但是還有一個問題,當我們研究大牛的代碼的時候,前面經常看到有“;”,那是為了避免代碼合并等不必要的錯誤。

例如,我們隨便定義一個函數:

var haoroomsblog=function(){}(function(){  })()

由于haoroomsblog這個函數后面沒有加分號,導致代碼出錯,為了避免這類情況的發生,通常這么寫!

;(function(){  })()

把你的插件代碼包裹在上面里面,就是一個簡單的插件了。(注js插件和jquery插件都是如此)

還有一個問題

把你的插件包裹在

;(function(){  })()

基本上可以說是完美了。但是為了讓你開發的插件應用更加廣泛,兼容性更加好,還要考慮到用插件的人的一些特殊的做法,例如,有些朋友為了避免jquery和zeptojs沖突,將jquery的前綴“$”,修改為“jQuery”,還有些朋友將默認的document等方法修改。為了讓你的插件在這些東西修了了的情況下照常運行,那么我們的做法是,把代碼包裹在如下里面:

;(function($,window,document,undefined){  //我們的代碼。。})(jQuery,window,document);

就可以避免上面的一些情況了!

優秀的示例
下面,我們有了一個插件的基本層次:

(function($) { var privateFunction = function() { // 代碼在這里運行 }  var methods = { init: function(options) {  return this.each(function() {  var $this = $(this);  var settings = $this.data('pluginName');   if(typeof(settings) == 'undefined') {    var defaults = {   propertyName: 'value',   onSomeEvent: function() {}   }    settings = $.extend({}, defaults, options);    $this.data('pluginName', settings);  } else {   settings = $.extend({}, settings, options);  }   // 代碼在這里運行   }); }, destroy: function(options) {  return $(this).each(function() {  var $this = $(this);   $this.removeData('pluginName');  }); }, val: function(options) {  var someValue = this.eq(0).html();   return someValue; } };  $.fn.pluginName = function() { var method = arguments[0];  if(methods[method]) {  method = methods[method];  arguments = Array.prototype.slice.call(arguments, 1); } else if( typeof(method) == 'object' || !method ) {  method = methods.init; } else {  $.error( 'Method ' + method + ' does not exist on jQuery.pluginName' );  return this; }  return method.apply(this, arguments);  } })(jQuery);

你可能會注意到,我所提到代碼的結構和其他插件代碼有很大的不同。根據你的使用和需求的不同,插件的開發方式也可能會呈現多樣化。我的目的是澄清代碼中的一些概念,足夠讓你找到適合自己的方法去理解和開發一個jQuery插件。
現在,來解剖我們的代碼吧!

容器:一個即時執行函數

根本上來說,每個插件的代碼是被包含在一個即時執行的函數當中,如下:

(function(arg1, arg2) {  // 代碼})(arg1, arg2);

即時執行函數,顧名思義,是一個函數。讓它與眾不同的是,它被包含在一對小括號里面,這讓所有的代碼都在匿名函數的局部作用域中運行。這并不是說DOM(全局變量)在函數內是被屏蔽的,而是外部無法訪問到函數內部的公共變量和對象命名空間。這是一個很好的開始,這樣你聲明你的變量和對象的時候,就不用擔心著變量名和已經存在的代碼有沖突。

現在,因為函數內部所有的所有公共變量是無法訪問的,這樣要把jQuery本身作為一個內部的公共變量來使用就會成為問題。就像普通的函數一樣,即時函數也根據引用傳入對象參數。我們可以將jQuery對象傳入函數,如下:

 (function($) {   // 局部作用域中使用$來引用jQuery})(jQuery);

我們傳入了一個把公共變量“jQuery”傳入了一個即時執行的函數里面,在函數局部(容器)中我們可以通過“$”來引用它。也就是說,我們把容器當做一個函數來調用,而這個函數的參數就是jQuery。因為我們引用的“jQuery”作為公共變量傳入,而不是它的簡寫“$”,這樣我們就可以兼容Prototype庫。如果你不用Prototype或者其它用“$”做簡寫的庫的話,你不這樣做也不會造成什么影響,但是知道這種用法仍是一件好事。

插件:一個函數

一個jQuery插件本質上是我們塞進jQuery命名空間中一個龐大的函數,當然,我們可以很輕易地用“jQuery.pluginName=function”,來達到我們的目的,但是如果我們這樣做的話我們的插件的代碼是處于沒有被保護的暴露狀態的?!癹Query.fn”是“jQuery.prototype”的簡寫,意味當我們通過jQuery命名空間去獲取我們的插件的時候,它僅可寫(不可修改)。它事實上可以為你干點什么事呢?它讓你恰當地組織自己的代碼,和理解如何保護你的代碼不受運行時候不需要的修改。最好的說法就是,這是一個很好的實踐!

通過一個插件,我們獲得一個基本的jQuery函數:

(function($) {  $.fn.pluginName = function(options) {  // 代碼在此處運行  return this; } })(jQuery);

上面的代碼中的函數可以像其他的jQuery函數那樣通過“$(‘#element').pluginName()”來調用。注意,我是如何把“return this”語句加進去的;這小片的代碼通過返回一個原來元素的集合(包含在this當中)的引用來產生鏈式調用的效果,而這些元素是被一個jQuery對象所包裹的。你也應該注意,“this”在這個特定的作用域中是一個jQuery對象,相當于“$(‘#element')”。

根據返回的對象,我們可以總結出,在上面的代碼中,使用“$(‘#element').pluginName()”的效果和使用“$(‘#element')”的效果是一樣的。在你的即時執行函數作用域中,沒必要用“$(this)”的方式來把this包裹到一個jQuery對象中,因為this本身已經是被包裝好的jQuery對象。

多個元素:理解Sizzle

jQuery使用的選擇器引擎叫Sizzle,Sizzle可以為你的函數提供多元素操作(例如對所有類名相同的元素)。這是jQuery幾個優秀的特性之一,但這也是你在開發插件過程中需要考慮的事情。即使你不準備為你的插件提供多元素支持,但為這做準備仍然是一個很好的實踐。

這里我添加了一小段代碼,它讓你的插件代碼為多元素集合中每個元素單獨地起作用:

function($) {  // 向jQuery中被保護的“fn”命名空間中添加你的插件代碼,用“pluginName”作為插件的函數名稱 $.fn.pluginName = function(options) {  // 返回“this”(函數each()的返回值也是this),以便進行鏈式調用。 return this.each(function() {   // 此處運行代碼,可以通過“this”來獲得每個單獨的元素  // 例如: $(this).show();  var $this = $(this);  });  } })(jQuery);

在以上示例代碼中,我并不是用 each()在我的選擇器中每個元素上運行代碼。在那個被 each()調用的函數的局部作用域中,你可以通過this來引用每個被單獨處理的元素,也就是說你可以通過$(this)來引用它的jQuery對象。在局部作用域中,我用$this變量存儲起jQuery對象,而不是每次調用函數的時候都使用$(this),這會是個很好的實踐。當然,這樣做并不總是必要的;但我已經額外把它包含在我的代碼中。還有要注意的是,我們將會對每個單獨方法都使用 each(),這樣到時我們就可以返回我們需要的值,而不是一個jQuery對象。

下面是一個例子,假如我們的插件支持一個 val 的方法,它可以返回我們需要的值:

$('#element').pluginName('val');// 會返回我們需要的值,而不是一個jQuery對象

功能:公有方法和私有方法

一個基本的函數可能在某些情況下可以良好地工作,但是一個稍微復雜一點的插件就需要提供各種各樣的方法和私有函數。你可能會使用不同的命名空間去為你的插件提供各種方法,但是最好不要讓你的源代碼因為多余的命名空間而變得混亂。

下面的代碼定義了一個存儲公有方法的JSON對象,以及展示了如何使用插件中的主函數中去判斷哪些方法被調用,和如何在讓方法作用到選擇器每個元素上。

(function($) {  // 在我們插件容器內,創造一個公共變量來構建一個私有方法 var privateFunction = function() { // code here }  // 通過字面量創造一個對象,存儲我們需要的共有方法 var methods = { // 在字面量對象中定義每個單獨的方法 init: function() {   // 為了更好的靈活性,對來自主函數,并進入每個方法中的選擇器其中的每個單獨的元素都執行代碼  return this.each(function() {  // 為每個獨立的元素創建一個jQuery對象  var $this = $(this);   // 執行代碼  // 例如: privateFunction();  }); }, destroy: function() {  // 對選擇器每個元素都執行方法  return this.each(function() {  // 執行代碼  }); } };  $.fn.pluginName = function() { // 獲取我們的方法,遺憾的是,如果我們用function(method){}來實現,這樣會毀掉一切的 var method = arguments[0];  // 檢驗方法是否存在 if(methods[method]) {   // 如果方法存在,存儲起來以便使用  // 注意:我這樣做是為了等下更方便地使用each()  method = methods[method];  // 如果方法不存在,檢驗對象是否為一個對象(JSON對象)或者method方法沒有被傳入 } else if( typeof(method) == 'object' || !method ) {   // 如果我們傳入的是一個對象參數,或者根本沒有參數,init方法會被調用  method = methods.init; } else {   // 如果方法不存在或者參數沒傳入,則報出錯誤。需要調用的方法沒有被正確調用  $.error( 'Method ' + method + ' does not exist on jQuery.pluginName' );  return this; }  // 調用我們選中的方法 // 再一次注意我們是如何將each()從這里轉移到每個單獨的方法上的 return method.call(this);  } })(jQuery);

注意我把 privateFunction 當做了一個函數內部的全局變量。考慮到所有的代碼的運行都是在插件容器內進行的,所以這種做法是可以被接受的,因為它只在插件的作用域中可用。在插件中的主函數中,我檢驗了傳入參數所指向的方法是否存在。如果方法不存在或者傳入的是參數為對象, init 方法會被運行。最后,如果傳入的參數不是一個對象而是一個不存在的方法,我們會報出一個錯誤信息。

同樣要注意的是,我是如何在每個方法中都使用 this.each() 的。當我們在主函數中調用 method.call(this) 的時候,這里的 this 事實上就是一個jQuery對象,作為 this 傳入每個方法中。所以在我們方法的即時作用域中,它已經是一個jQuery對象。只有在被 each()所調用的函數中,我們才有必要將this包裝在一個jQuery對象中。

下面是一些用法的例子:

/* 注意這些例子可以在目前的插件代碼中正確運行,并不是所有的插件都使用同樣的代碼結構 */// 為每個類名為 ".className" 的元素執行init方法$('.className').pluginName();$('.className').pluginName('init');$('.className').pluginName('init', {}); // 向init方法傳入“{}”對象作為函數參數$('.className').pluginName({}); // 向init方法傳入“{}”對象作為函數參數 // 為每個類名為 “.className” 的元素執行destroy方法$('.className').pluginName('destroy');$('.className').pluginName('destroy', {}); // 向destroy方法傳入“{}”對象作為函數參數 // 所有代碼都可以正常運行$('.className').pluginName('init', 'argument1', 'argument2'); // 把 "argument 1" 和 "argument 2" 傳入 "init" // 不正確的使用$('.className').pluginName('nonexistantMethod');$('.className').pluginName('nonexistantMethod', {});$('.className').pluginName('argument 1'); // 會嘗試調用 "argument 1" 方法$('.className').pluginName('argument 1', 'argument 2'); // 會嘗試調用 "argument 1" ,“argument 2”方法$('.className').pluginName('privateFunction'); // 'privateFunction' 不是一個方法

在上面的例子中多次出現了 {} ,表示的是傳入方法中的參數。在這小節中,上面代碼可以可以正常運行,但是參數不會被傳入方法中。繼續閱讀下一小節,你會知道如何向方法傳入參數。   設置插件:傳入參數 許多插件都支持參數傳入,如配置參數和回調函數。你可以通過傳入JS鍵值對對象或者函數參數,為方法提供信息。如果你的方法支持多于一個或兩個參數,那么沒有比傳入對象參數更恰當的方式。

(function($) { var methods = { init: function(options) {   // 在每個元素上執行方法  return this.each(function() {  var $this = $(this);   // 創建一個默認設置對象  var defaults = {   propertyName: 'value',   onSomeEvent: function() {}  }   // 使用extend方法從options和defaults對象中構造出一個settings對象  var settings = $.extend({}, defaults, options);   // 執行代碼   }); } };  $.fn.pluginName = function() { var method = arguments[0];  if(methods[method]) {  method = methods[method];   // 我們的方法是作為參數傳入的,把它從參數列表中刪除,因為調用方法時并不需要它  arguments = Array.prototype.slice.call(arguments, 1); } else if( typeof(method) == 'object' || !method ) {  method = methods.init; } else {  $.error( 'Method ' + method + ' does not exist on jQuery.pluginName' );  return this; }  // 用apply方法來調用我們的方法并傳入參數 return method.apply(this, arguments);  } })(jQuery);

正如上面所示,一個“options”參數被添加到方法當中,和“arguments”也被添加到了主函數中。如果一個方法已經被聲明,在參數傳入方法之前,調用那個方法的參數會從參數列表中刪除掉。我用了“apply()”來代替了“call()”,“apply()”本質上是和“call()”做著同樣的工作的,但不同的是它允許參數的傳入。這種結構也允許多個參數的傳入,如果你愿意這樣做,你也可以為你的方法修改參數列表,例如:“init:function(arg1, arg2){}”。


如果你是使用JS對象作為參數傳入,你可能需要定義一個默認對象。一旦默認對象被聲明,你可以使用“$.extend”來合并參數對象和默認對象中的值,以形成一個新的參數對象來使用(在我們的例子中就是“settings”);

這里有一些例子,用來演示以上的邏輯:

var options = { customParameter: 'Test 1', propertyName: 'Test 2'} var defaults = { propertyName: 'Test 3', onSomeEvent: 'Test 4'} var settings = $.extend({}, defaults, options);/*settings == { propertyName: 'Test 2', onSomeEvent: 'Test 4', customParameter: 'Test 1'}*/

保存設置:添加持久性數據

有時你會想在你的插件中保存設置和信息,這時jQuery中的“data()”函數就可以派上用場了。它在使用上是非常簡單的,它會嘗試獲取和元素相關的數據,如果數據不存在,它就會創造相應的數據并添加到元素上。一旦你使用了“data()”來為元素添加信息,請確認你已經記住,當不再需要數據的時候,用“removeData()”來刪除相應的數據。

(function($) { var privateFunction = function() { // 執行代碼 }  var methods = { init: function(options) {   // 在每個元素上執行方法  return this.each(function() {  var $this = $(this);   // 嘗試去獲取settings,如果不存在,則返回“undefined”  var settings = $this.data('pluginName');   // 如果獲取settings失敗,則根據options和default創建它  if(typeof(settings) == 'undefined') {    var defaults = {   propertyName: 'value',   onSomeEvent: function() {}   }    settings = $.extend({}, defaults, options);    // 保存我們新創建的settings   $this.data('pluginName', settings);  } else {   / 如果我們獲取了settings,則將它和options進行合并(這不是必須的,你可以選擇不這樣做)   settings = $.extend({}, settings, options);    // 如果你想每次都保存options,可以添加下面代碼:   // $this.data('pluginName', settings);  }   // 執行代碼   }); }, destroy: function(options) {  // 在每個元素中執行代碼  return $(this).each(function() {  var $this = $(this);   // 執行代碼   // 刪除元素對應的數據  $this.removeData('pluginName');  }); }, val: function(options) {  // 這里的代碼通過.eq(0)來獲取選擇器中的第一個元素的,我們或獲取它的HTML內容作為我們的返回值  var someValue = this.eq(0).html();   // 返回值  return someValue; } };  $.fn.pluginName = function() { var method = arguments[0];  if(methods[method]) {  method = methods[method];  arguments = Array.prototype.slice.call(arguments, 1); } else if( typeof(method) == 'object' || !method ) {  method = methods.init; } else {  $.error( 'Method ' + method + ' does not exist on jQuery.pluginName' );  return this; }  return method.apply(this, arguments);  } })(jQuery);

在上面的代碼中,我檢驗了元素的數據是否存在。如果數據不存在,“options”和“default”會被合并,構建成一個新的settings,然后用“data()”保存在元素中。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美最顶级丰满的aⅴ艳星| 国产91精品最新在线播放| 久99九色视频在线观看| 欧美一级片久久久久久久| 久久久精品久久久| 精品亚洲永久免费精品| 精品久久久一区| 色妞久久福利网| 国产欧美在线看| 亚洲女人天堂色在线7777| 亚洲成人久久网| 第一福利永久视频精品| 91久久综合亚洲鲁鲁五月天| 欧美成人三级视频网站| 亚洲护士老师的毛茸茸最新章节| 一区二区三区天堂av| 91理论片午午论夜理片久久| 国产一区二区视频在线观看| 成人免费视频在线观看超级碰| 精品久久久久久国产91| 精品自拍视频在线观看| 国产成人一区二区三区电影| 日韩**中文字幕毛片| 久久大大胆人体| 8x拔播拔播x8国产精品| 久热精品视频在线观看一区| 亚洲欧洲国产一区| 97人人爽人人喊人人模波多| 成人中文字幕+乱码+中文字幕| 欧美性猛交视频| 日本一区二区三区四区视频| 欧美性猛交xxxx乱大交极品| 亚洲精品自拍偷拍| 91亚洲精华国产精华| 丝袜亚洲欧美日韩综合| 国产精品第七十二页| 国产精品影片在线观看| 国产这里只有精品| 国产视频精品一区二区三区| 国产成人综合精品| 亚洲福利视频在线| 国产精品丝袜视频| 国外色69视频在线观看| 国产亚洲成精品久久| 国产午夜精品免费一区二区三区| 日韩有码片在线观看| 亚洲xxxx在线| 国产视频亚洲精品| 久久久久国产精品免费网站| 精品视频在线导航| 91香蕉亚洲精品| 欧美高清视频免费观看| 国产美女主播一区| 一区二区av在线| 国产精品69久久| 麻豆国产va免费精品高清在线| 国产精品久久久久久久app| 欧美中在线观看| 欧美在线观看www| 国产成人涩涩涩视频在线观看| 热99久久精品| 欧美成人第一页| 九色精品免费永久在线| 中文字幕欧美精品在线| 精品久久久久久中文字幕大豆网| 91久久精品一区| 欧美在线视频免费观看| 这里精品视频免费| 久久久午夜视频| 韩日欧美一区二区| 国产有码在线一区二区视频| 91亚洲午夜在线| 亚洲国产91色在线| 亚洲系列中文字幕| 国产亚洲人成网站在线观看| 欧美精品久久久久久久久| 午夜精品免费视频| 欧美一级电影在线| 两个人的视频www国产精品| 成人欧美一区二区三区黑人| 色综合影院在线| 色综合久久精品亚洲国产| 精品日韩中文字幕| 日韩欧美极品在线观看| 亚洲女人天堂成人av在线| 国产亚洲精品美女久久久| 亚洲三级黄色在线观看| 久久999免费视频| 国产亚洲一区二区在线| 日韩一区二区三区国产| 亚洲a级在线观看| 97精品久久久中文字幕免费| 日韩美女免费观看| 91免费在线视频| 久久精品人人爽| 亚洲欧洲午夜一线一品| 成人免费看片视频| 久久久久久久久久久国产| 亚洲xxxx做受欧美| 久久久久久18| 成人午夜黄色影院| 亚洲精品动漫久久久久| 在线观看免费高清视频97| 欧美黄色片在线观看| 成人国产在线视频| 久久综合电影一区| 久久资源免费视频| 亚洲jizzjizz日本少妇| 亚洲欧美中文在线视频| 国产z一区二区三区| 日本道色综合久久影院| 亚洲全黄一级网站| 亚洲在线视频福利| 日本高清久久天堂| 一本一道久久a久久精品逆3p| 热久久美女精品天天吊色| 成人av色在线观看| 亚洲一区二区日本| 久久久久久香蕉网| 国产精品一区二区三区久久久| 欧美视频在线免费| 亚洲色图激情小说| 欧洲s码亚洲m码精品一区| 久久久久国色av免费观看性色| 日韩欧美成人免费视频| 欧美激情网友自拍| 九九热在线精品视频| 亚洲天堂久久av| 欧美午夜精品伦理| 热久久免费国产视频| 久久色在线播放| 日本精品va在线观看| 欧美国产第二页| 欧美日韩国产精品一区二区不卡中文| 精品丝袜一区二区三区| 欧美在线视频在线播放完整版免费观看| 中文字幕欧美日韩| 一级做a爰片久久毛片美女图片| 日韩视频永久免费观看| 欧美成年人视频网站欧美| 68精品国产免费久久久久久婷婷| 久热99视频在线观看| 亚洲国产精品福利| 亚洲国产成人久久综合一区| 国产aⅴ夜夜欢一区二区三区| 欧美精品videos性欧美| 亚洲视频视频在线| 亚洲精品国产精品国自产在线| 91在线无精精品一区二区| 亚洲一区二区在线播放| 91精品在线播放| 色一情一乱一区二区| 国产日韩综合一区二区性色av| 国产激情久久久久| 亚洲性日韩精品一区二区| 国产精品吹潮在线观看| 久久精品视频在线观看| 欧美一级在线播放| 亚洲欧美中文日韩v在线观看| 91精品国产自产91精品| 最近2019中文字幕在线高清| 韩国国内大量揄拍精品视频| 91精品久久久久久| 免费99精品国产自在在线|