日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

jQuery-1.9.1源码分析系列(五) 回调对象

發(fā)布時(shí)間:2023/12/19 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jQuery-1.9.1源码分析系列(五) 回调对象 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  jQuery.Callbacks()提供的回調(diào)函數(shù)隊(duì)列管理本來(lái)是延時(shí)回調(diào)處理的一部分,但是后面將其獨(dú)立出來(lái)作為一個(gè)模塊。jQuery就是這樣,各個(gè)模塊間的代碼耦合度是處理的比較好的,值得學(xué)習(xí)。雖然是從延時(shí)回調(diào)處理中獨(dú)立出來(lái)的,但是它的功能非常強(qiáng)大,提供了一種強(qiáng)大的方法來(lái)管理回調(diào)函數(shù)隊(duì)列。

  大家都明白封裝函數(shù)的目的:去耦合與簡(jiǎn)化操作

  通常情況下函數(shù)隊(duì)列的處理方式

//執(zhí)行函數(shù) function runList(arr){for(var i = 0; i < arr.length; i++){arr[i]();    }
  arr.length = 0; }
var list = []; //添加函數(shù)隊(duì)列 list[list.length] = function(){alert(1)}; list[list.length] = function(){alert(2)};
list[list.length] = function(){alert(3)}; //執(zhí)行
runList(list);//三個(gè)函數(shù)順序執(zhí)行

  使用$.callbacks封裝以后的處理為

var callbacks = $.Callbacks("unique");callbacks.add( function(){alert(1)} ); callbacks.add( function(){alert(2)} ); callbacks.add( function(){alert(3)} ); //執(zhí)行 callbacks.fire();//三個(gè)函數(shù)順序執(zhí)行

  干凈了很多。而且代碼可讀性比最開(kāi)始的那個(gè)要好很多。list[list.length]神馬的最討厭了。還有主要的是$.callbacks有四個(gè)屬性可以組合,這個(gè)組合可就很強(qiáng)大了。

  

a. Callbacks的四個(gè)可設(shè)置的屬性分析


?

once: 確保這個(gè)回調(diào)列表只執(zhí)行( .fire() )一次(像一個(gè)遞延 Deferred).

?????? 設(shè)置“once”在執(zhí)行第一次fire后會(huì)直接禁用該Callbacks(fire函數(shù)代碼段else {self.disable();})

var f1 = function(value) { console.log(value); }; var callbacks = $.Callbacks('once');callbacks.add(f1);//無(wú)執(zhí)行結(jié)果,添加一個(gè)回調(diào) callbacks.fire(1);//執(zhí)行結(jié)果1。清除回調(diào)列表 callbacks.add(f1);//沒(méi)有添加回調(diào)直接返回 callbacks.fire(2);//無(wú)執(zhí)行結(jié)果 callbacks.add(f1);//沒(méi)有添加回調(diào)直接返回 callbacks.fire(3);//無(wú)執(zhí)行結(jié)果

?

memory:?保持以前的值(參數(shù)),將函數(shù)添加到這個(gè)列表的后面,并使用先前保存的參數(shù)立即執(zhí)行該函數(shù)。 內(nèi)部變量會(huì)保存上次執(zhí)行的場(chǎng)景。

  他有一個(gè)特點(diǎn),就是在第一次fire之前使用add添加的回調(diào)都不會(huì)馬上執(zhí)行,只有調(diào)用了一次fire之后使用add添加的回調(diào)會(huì)馬上執(zhí)行。該設(shè)置本身不會(huì)清除之前的回調(diào)列表。

  需要注意的是每次add內(nèi)部執(zhí)行fire函數(shù)都會(huì)將firingStart置為0,只有下次add的時(shí)候會(huì)從新設(shè)置firingStart的值。

  eg:

var f1 = function(value) { console.log(value); }; var callbacks = $.Callbacks("memory");callbacks.add( fn1 );//無(wú)執(zhí)行結(jié)果 callbacks.fire( "1" );//執(zhí)行結(jié)果1。保存場(chǎng)景參數(shù)1 callbacks.add( fn1 );//執(zhí)行結(jié)果1。使用上次保存的場(chǎng)景參數(shù)1 callbacks.fire( "2" );//執(zhí)行結(jié)果2,2。保存場(chǎng)景參數(shù)2 callbacks.add( fn1 );//執(zhí)行結(jié)果2。使用上次保存的場(chǎng)景參數(shù)2 callbacks.fire( "3" );//執(zhí)行結(jié)果3,3,3。保存場(chǎng)景參數(shù)3 callbacks.add( fn1 );//執(zhí)行結(jié)果3。使用上次保存的場(chǎng)景參數(shù)3 callbacks.fire( "4" );//執(zhí)行結(jié)果4,4,4,4。保存場(chǎng)景參數(shù)4

  組合使用,組合使用中間使用空格隔開(kāi)

  設(shè)置“once memory”, options.once=options.memory=true。在執(zhí)行第一次fire后會(huì)把回到列表清空,而且之后每次add馬上執(zhí)行后頁(yè)同樣會(huì)把回調(diào)列表清空(fire函數(shù)代碼段else if ( memory ) {list = [];})。

?????? eg:

var f1 = function(value) { console.log(value); }; var callbacks = $.Callbacks('once memory');callbacks.add(f1);//無(wú)執(zhí)行結(jié)果,添加一個(gè)回調(diào) callbacks.fire(1);//執(zhí)行結(jié)果1。清除回調(diào)列表,保存場(chǎng)景參數(shù)1 callbacks.add(f1);//添加一個(gè)回調(diào)并執(zhí)行結(jié)果1,使用上次保存的場(chǎng)景參數(shù)。清除回調(diào)列表 callbacks.fire(2);//無(wú)執(zhí)行結(jié)果 callbacks.add(f1);//添加一個(gè)回調(diào)并執(zhí)行結(jié)果1,使用上次保存的場(chǎng)景參數(shù)。清除回調(diào)列表 callbacks.fire(3);//無(wú)執(zhí)行結(jié)果

  兩個(gè)設(shè)置之間用空格,不支持其他符號(hào),比如設(shè)置“once,memory”等同于沒(méi)有設(shè)置。??????

  eg:

var f1 = function(value) { console.log(value); }; var callbacks = $.Callbacks('once,memory');callbacks.add(f1);//無(wú)執(zhí)行結(jié)果,添加一個(gè)回調(diào) callbacks.fire(1);//執(zhí)行結(jié)果1 callbacks.add(f1);//添加一個(gè)回調(diào) callbacks.fire(2);//執(zhí)行結(jié)果2,2 callbacks.add(f1);//添加一個(gè)回調(diào) callbacks.fire(3);//執(zhí)行結(jié)果3,3,3 callbacks.add(f1);//添加一個(gè)回調(diào) callbacks.fire(4);//執(zhí)行結(jié)果4,4,4,4

?

unique: 確保一次只能添加一個(gè)回調(diào)(所以在列表中沒(méi)有重復(fù)的回調(diào)).

?

stopOnFalse: 當(dāng)一個(gè)回調(diào)返回false 時(shí)中斷調(diào)用

?????? 當(dāng)有一個(gè)回調(diào)返回false的時(shí)候,會(huì)設(shè)置memory為false。導(dǎo)致memory失去作用(后續(xù)add的函數(shù)不會(huì)馬上執(zhí)行,當(dāng)然先前memory保證了前面執(zhí)行過(guò)得函數(shù)不再執(zhí)行這也條也就不起作用了。下次fire會(huì)從回調(diào)列表的第一個(gè)開(kāi)始執(zhí)行)。

?

b. 整體結(jié)構(gòu)


  使用緩存是jQuery中最常見(jiàn)的技巧。$.Callbacks中也不例外。主要是緩存Callbacks中遇到的選項(xiàng)(字符串)。

// 使用過(guò)的選項(xiàng)緩存 var optionsCache = {};// 新增和緩存回調(diào)設(shè)置于optionsCache中 function createOptions( options ) {var object = optionsCache[ options ] = {};jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {object[ flag ] = true;});return object; }jQuery.Callbacks = function( options ) {// 盡可能讀取緩存,沒(méi)有則新增緩存options = typeof options === "string" ?( optionsCache[ options ] || createOptions( options ) ) :jQuery.extend( {}, options );var // 回調(diào)列表正在執(zhí)行(為true的時(shí)候)的標(biāo)志 firing,// 最后執(zhí)行的值(為memory選項(xiàng)下保存) memory,// 回調(diào)已經(jīng)被執(zhí)行過(guò)的標(biāo)志 fired,// 循環(huán)執(zhí)行回調(diào)列表的結(jié)束位置 firingLength,// 當(dāng)前真正執(zhí)行的回調(diào)的索引值 (執(zhí)行下個(gè)回調(diào)的時(shí)候回更改【如果必要的話】) firingIndex,// 循環(huán)執(zhí)行回調(diào)列表的開(kāi)始位置(在函數(shù)add和fireWith中使用) firingStart,// 回調(diào)列表list = [],// Stack記錄要重復(fù)執(zhí)行的回調(diào)列表stack = !options.once && [],// data數(shù)組一般第一個(gè)元素是上下文環(huán)境,第二個(gè)元素是參數(shù)//執(zhí)行回調(diào)列表fire = function( data ) {…},// 回調(diào)對(duì)象self = {// 添加回調(diào)add: function() {…},// 移除回調(diào)remove: function() {…},...// 給定 context 和 arguments執(zhí)行所有回調(diào)fireWith: function( context, args ) {args = args || [];//組裝args,第一個(gè)元素為上下文環(huán)境,第二個(gè)元素為參數(shù)列表args = [ context, args.slice ? args.slice() : args ];//有l(wèi)ist且函數(shù)列表沒(méi)有被執(zhí)行過(guò)或者存在要循環(huán)執(zhí)行的函數(shù)列表if ( list && ( !fired || stack ) ) {//如果正在fire,則把函數(shù)場(chǎng)景記錄在stack中if ( firing ) {stack.push( args );//否則,至此那個(gè)fire} else {fire( args );}}return this;},// 使用給定的arguments執(zhí)行所有回調(diào)fire: function() {self.fireWith( this, arguments );return this;},...};return self; };

  

  下面分析兩個(gè)最重要的兩個(gè)函數(shù),添加回調(diào)函數(shù)add和執(zhí)行回調(diào)函數(shù)fire

c. add:添加回調(diào)


  添加回調(diào)函數(shù)比較簡(jiǎn)單,針對(duì)可能傳遞的值(函數(shù)或者函數(shù)數(shù)組)將回調(diào)添加到回調(diào)列表中即可,這里使用了一個(gè)閉包,使用了外部變量list。

(function add( args ) {jQuery.each( args, function( _, arg ) {var type = jQuery.type( arg );if ( type === "function" ) {//當(dāng)$.Callbacks('unique')時(shí),保證列表里面不會(huì)出現(xiàn)重復(fù)的回調(diào)if ( !options.unique || !self.has( arg ) ) {list.push( arg );}//如果是數(shù)組則遞歸添加} else if ( arg && arg.length && type !== "string" ) {add( arg );}});})( arguments );

  但是這里需要對(duì)用戶(hù)初始化設(shè)置的屬性做一些特殊的處理。

  如果列表沒(méi)有定義或null(一般只有在用戶(hù)設(shè)置once且執(zhí)行過(guò)一次后list才會(huì)白置為未定義),直接返回list

//如果列表沒(méi)有定義或null(一般只有在用戶(hù)設(shè)置once且執(zhí)行過(guò)一次后list才會(huì)白置為未定義)if ( list ) {...}return this;

  當(dāng)有回調(diào)真正執(zhí)行的時(shí)候,需要重新設(shè)定回調(diào)列表的結(jié)束位置firingLength,使后續(xù)添加的函數(shù)也會(huì)執(zhí)行。實(shí)際上這個(gè)功能很受爭(zhēng)議,不過(guò)正常情況一般不會(huì)出現(xiàn)添加函數(shù)的時(shí)候正在執(zhí)行某個(gè)回調(diào)。

  還有一個(gè)比較重要的判斷:對(duì)于設(shè)置了'memory'選項(xiàng)并fire過(guò)了回調(diào)列表,并且沒(méi)有還在等待中的回調(diào)要fire,則應(yīng)當(dāng)馬上執(zhí)行新添加的回調(diào)(執(zhí)行fire(memory))

// 如果正在fire,則設(shè)定要執(zhí)行結(jié)束的點(diǎn)firingLength,使后續(xù)添加的函數(shù)最后不會(huì)執(zhí)行if ( firing ) {firingLength = list.length;// 對(duì)于memory(設(shè)置了'memory' option并fire過(guò)了,memory才能通過(guò)該else if語(yǔ)句),//如果沒(méi)有回調(diào)真正fire,應(yīng)當(dāng)馬上執(zhí)行fire(memory)。} else if ( memory ) {//這里保證了前面執(zhí)行過(guò)得函數(shù)不再執(zhí)行firingStart = start;fire( memory );}

  完整的源碼如下

add: function() {//如果列表沒(méi)有定義或null(一般只有在用戶(hù)設(shè)置once且執(zhí)行過(guò)一次后list才會(huì)白置為未定義)if ( list ) {// 保存當(dāng)前l(fā)ist長(zhǎng)度,為memory處理備用var start = list.length;(function add( args ) {jQuery.each( args, function( _, arg ) {var type = jQuery.type( arg );if ( type === "function" ) {//當(dāng)$.Callbacks('unique')時(shí),保證列表里面不會(huì)出現(xiàn)重復(fù)的回調(diào)if ( !options.unique || !self.has( arg ) ) {list.push( arg );}//如果是數(shù)組則遞歸添加} else if ( arg && arg.length && type !== "string" ) {add( arg );}});})( arguments );// 如果正在fire,則設(shè)定要執(zhí)行結(jié)束的點(diǎn)firingLength,使后續(xù)添加的函數(shù)最后執(zhí)行if ( firing ) {firingLength = list.length;// 對(duì)于memory(設(shè)置了'memory' option并fire過(guò)了,memory才能通過(guò)該else if語(yǔ)句),//如果我們后續(xù)沒(méi)有fire,應(yīng)當(dāng)馬上執(zhí)行fire(memory)。} else if ( memory ) {//這里保證了前面執(zhí)行過(guò)得函數(shù)不再執(zhí)行firingStart = start;fire( memory );}}return this; } View Code

  

d. fire函數(shù)詳解


  該函數(shù)執(zhí)行回調(diào),最終執(zhí)行代碼段為

if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {memory = false; // 阻止未來(lái)可能由于add所產(chǎn)生的回調(diào)break; }

?

fire = function( data ) {   //有memory才給memory賦值當(dāng)前場(chǎng)景datamemory = options.memory && data;fired = true;firingIndex = firingStart || 0;   //每次fire后都會(huì)重置成0,下次$.callbacks.fire調(diào)用都會(huì)從0開(kāi)始。當(dāng)然設(shè)置為‘memory’使用add函數(shù)內(nèi)部fire會(huì)設(shè)置firingStart的值導(dǎo)致回調(diào)函數(shù)列表執(zhí)行起始位置更改firingStart = 0;firingLength = list.length;firing = true;
  //函數(shù)開(kāi)始執(zhí)行從firingStart到firingLength的所有函數(shù)   for ( ; list && firingIndex < firingLength; firingIndex++ ) {     //執(zhí)行firingIndex對(duì)應(yīng)的函數(shù),如果設(shè)置是遇到false返回就停止,則設(shè)置memory,阻止后續(xù)函數(shù)執(zhí)行     if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {memory = false; // 阻止未來(lái)可能由于add所產(chǎn)生的回調(diào)       break;}}  //標(biāo)記回調(diào)結(jié)束firing = false;//如果列表存在   if ( list ) {     //如果堆棧存在(一般沒(méi)有設(shè)置once的時(shí)候都進(jìn)入該分支)     if ( stack ) {       //如果堆棧不為空       if ( stack.length ) {         //執(zhí)行stack中第一個(gè)元素fire( stack.shift() );}     //如果有記憶,則清空列表(在設(shè)置為once且memory的時(shí)候會(huì)進(jìn)入到此分支)} else if ( memory ) {list = [];     //禁用回調(diào),該callbacks將不可用,將list/stack/memory都設(shè)為未定義} else {self.disable();}} },

  真正重要的是執(zhí)行完成回調(diào)以后的處理

  //如果列表存在if ( list ) {//如果堆棧存在(一般沒(méi)有設(shè)置once的時(shí)候都進(jìn)入該分支)if ( stack ) {//如果堆棧不為空if ( stack.length ) {//執(zhí)行stack中第一個(gè)元素         fire( stack.shift() );}//如果有記憶,則清空列表(在設(shè)置為once且memory的時(shí)候會(huì)進(jìn)入到此分支)} else if ( memory ) {list = [];//禁用回調(diào),該callbacks將不可用,將list/stack/memory都設(shè)為未定義} else {self.disable();}} View Code

  首先看最外層的判斷

if ( list ){... }

?

  什么時(shí)候會(huì)進(jìn)不了這個(gè)分支呢?唯有當(dāng)self.disable()被調(diào)用的時(shí)候,下一次fire就進(jìn)入不了這個(gè)分支。查看self.disable源碼

disable: function() {list = stack = memory = undefined;return this;}

  根據(jù)里面的判斷唯有當(dāng)options選項(xiàng)有once,并且選項(xiàng)中沒(méi)有memory或選項(xiàng)中有stopOnFalse且執(zhí)行的回調(diào)返回false。這個(gè)時(shí)候回進(jìn)入到里面的分支直接將整個(gè)回調(diào)禁用掉。

    //禁用回調(diào),該callbacks將不可用,將list/stack/memory都設(shè)為未定義} else {self.disable();}

  第一個(gè)內(nèi)部分支if ( stack )主要是選項(xiàng)中沒(méi)有once就進(jìn)入。

  第二個(gè)內(nèi)部分支只有在選項(xiàng)至少有once和memory的時(shí)候才會(huì)進(jìn)入。當(dāng)然,如果還有stopOnFalse且執(zhí)行的回調(diào)返回false會(huì)進(jìn)入到第三個(gè)分支。

    //如果有記憶,則清空列表(在設(shè)置為once且memory的時(shí)候會(huì)進(jìn)入到此分支)} else if ( memory ) {

?

  好了,這個(gè)jQuery.Callbacks就到這里。需要注意的就是多個(gè)選項(xiàng)混合使用要特別小心。

?

  如果覺(jué)得本文不錯(cuò),請(qǐng)點(diǎn)擊右下方【推薦】!

?

轉(zhuǎn)載于:https://www.cnblogs.com/chuaWeb/p/jQuery-1-9-1-Callbacks.html

總結(jié)

以上是生活随笔為你收集整理的jQuery-1.9.1源码分析系列(五) 回调对象的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。