博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jQuery回调、递延对象总结(一)jQuery.Callbacks详解
阅读量:6424 次
发布时间:2019-06-23

本文共 13472 字,大约阅读时间需要 44 分钟。

前言:作为参数传递给另一个函数执行的函数我们称为回调函数,那么该回调又是否是异步的呢,何谓异步,如:作为事件处理器,或作为参数传递给(setTimeout,setInterval)这样的异步函数,或作为ajax发送请求,应用于请求各种状态的处理,我们可以称为异步回调,jQuery.Callbacks为我们封装了一个回调对象模块,我们先来看一个应用场景: // 为什么jQuery中的ready事件可以执行多个回调,这得益于我们的jQuery.Deferred递延对象(是基于jQuery.Callbacks回调模块)  jQuery(function($) {      console.log('document is ready!');      // do something  });   jQuery(function($) {      // do something  });   // 实现原型  // jQuery.Deferred版代码  var df = jQuery.Deferred();  df.resolve(); // 在ready事件中调用   // 可以多次执行绑定的函数  df.done(fn1, fn2, fn3);  df.done(fn4);  // ...    // jQuery.Callbacks版代码  var cb = jQuery.Callbacks('once memory');  cb.fire(); // 在ready事件中调用   // 可以多次执行绑定的函数  cb.add(fn1, fn2, fn3);  cb.add(fn4);  // ... 现在我们知道jQuery中的ready事件是可以这样执行多个回调的,要想深入理解其源码,让我们继续看下面吧 一、jQuery.Callbacks设计思路使用一个私有变量list(数组)存储回调,执行jQuery.Callbacks函数将返回一个可以操作回调列表list的接口对象,而传入jQuery.Callbacks函数的options参数则用来控制返回的回调对象操作回调列表的行为 回调对象中的方法 {      add:        增加回调到list中      remove:     从list中移除回调      fire:       触发list中的回调      fired:      回调对象是否执行过fire方法      fireWith:   触发list中的回调,第一个参数为执行域      has:        判断函数是否在list中      empty:      将list致空,list = [];      lock:       锁定list      locked:     是否锁定      disable:    禁用回调对象      disabled:   是否禁用  } 参数标志:options = {      once:       回调对象仅触发(fire)一次       memory:     跟踪记录每一次传递给fire函数的参数,在回调对象触发后(fired),                  将最后一次触发(fire)时的参数(value)传递给在add操作后即将被调用的回调       unique:     在add操作中,相同的函数仅只一次被添加(push)到回调列表中       stopOnFalse:当回调函数返回false,中断列表中的回调循环调用,且memory === false,阻止在add操作中将要触发的回调  }

 

二、源码解析    var // Used for splitting on whitespace          core_rnotwhite = /\S+/g;           var optionsCache = {};           // Convert String-formatted options into Object-formatted ones and store in cache      // 将字符串格式选项转化成对象格式形式,并存储在缓存对象optionsCache[options]中      // 该缓存起作用适用于执行多次jQuery.Callbacks函数,且传递options参数一致,我们在jQuery.Deferred      // 源码就可以看到tuples二维数组中执行了两次jQuery.Callbacks('once memory')      function createOptions( options ) {          var object = optionsCache[ options ] = {};          jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {              object[ flag ] = true;          });          return object;      }      jQuery.Callbacks = function( options ) {               // Convert options from String-formatted to Object-formatted if needed          // (we check in cache first)          options = typeof options === "string" ?              ( optionsCache[ options ] || createOptions( options ) ) :              jQuery.extend( {}, options );               var // Flag to know if list is currently firing              firing,              // Last fire value (for non-forgettable lists)              memory,              // Flag to know if list was already fired              fired,              // End of the loop when firing              firingLength,              // Index of currently firing callback (modified by remove if needed)              firingIndex,              // First callback to fire (used internally by add and fireWith)              firingStart,              // Actual callback list              list = [],              // Stack of fire calls for repeatable lists              stack = !options.once && [],              // Fire callbacks              fire = function( data ) {                  memory = options.memory && data;                  fired = true;                  firingIndex = firingStart || 0;                  firingStart = 0;                  firingLength = list.length;                  firing = true;                  // 迭代list回调列表,列表中的回调被应用(或执行回调)                  for ( ; list && firingIndex < firingLength; firingIndex++ ) {                      // 如果回调返回false,且options.stopOnFlase === true,则中断循环                      // 注:data[1]是一个伪数组(self.fire方法中的arguments(参数集合))                      if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {                                              memory = false; // To prevent further calls using add   // 阻止在add操作中可能执行的回调                          break;                      }                  }                  firing = false;                  if ( list ) {                      // (options.once === undefined),回调对象可以触发多次                      if ( stack ) {                          // 处理正在执行的回调中的fireWith操作                          // 注:如果执行的回调中真的拥有fire或fireWith操作,那么列表中的回调将会无限循环的执行,请看实例1                          if ( stack.length ) {                              fire( stack.shift() );                          }                      }                      // (options.once === true && options.memory === true)                      // 回调列表致空,但允许add继续添加并执行回调                      else if ( memory ) {                          list = [];                      }                      // (options.once === true && options.memory === undefined)                      // 禁用回调对象                      else {                          self.disable();                      }                  }              },              // Actual Callbacks object              self = {                  // Add a callback or a collection of callbacks to the list                  add: function() {                      if ( list ) {                          // First, we save the current length                          var start = list.length;                          (function add( args ) {                              jQuery.each( args, function( _, arg ) {                                  var type = jQuery.type( arg );                                  if ( type === "function" ) {                                      // 回调不唯一 或 唯一且不存在,则push                                      if ( !options.unique || !self.has( arg ) ) {                                          list.push( arg );                                      }                                  } else if ( arg && arg.length && type !== "string" ) {                                      // Inspect recursively                                      // 递归检查                                      add( arg );                                  }                              });                          })( arguments );                               // Do we need to add the callbacks to the                          // current firing batch?                          // 如果正在执行的回调执行了add操作,更新firingLength,将列表中新加进来的最后一个回调加入到回调执行的队列中                          if ( firing ) {                              firingLength = list.length;                               // With memory, if we're not firing then                          // we should call right away                          // 如果可能(options.memory === true),在回调对象不能再次fire(options.once === true)时,                          // 我们应该使用memory(记录的最后一次fire时的参数)立即调用回调                          } else if ( memory ) {                              firingStart = start;                              fire( memory );                          }                      }                      return this;                  },                  // Remove a callback from the list                  remove: function() {                      if ( list ) {                          jQuery.each( arguments, function( _, arg ) {                              var index;                              while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {                                  list.splice( index, 1 );                                  // Handle firing indexes                                  // 在回调对象触发(fire)时,如果firingLength、firingIndex(正在执行的回调在列表list中的索引index)                                  // 大于等于 移除的回调的索引(index),分别减一,确保回调执行队列中未执行的回调依次执行                                  if ( firing ) {                                      if ( index <= firingLength ) {                                          firingLength--;                                      }                                      if ( index <= firingIndex ) {                                          firingIndex--;                                      }                                  }                              }                          });                      }                      return this;                  },                  // Check if a given callback is in the list.                  // If no argument is given, return whether or not list has callbacks attached.                  // 检查给定的回调是否在列表中                  // 如果未给定回调参数,返回列表是否有回调                  has: function( fn ) {                      return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );                  },                  // Remove all callbacks from the list                  // 将列表致空,list = []; firingLenght = 0;                  empty: function() {                      list = [];                      firingLength = 0;                      return this;                  },                  // Have the list do nothing anymore                  // 禁用回调对象                  // 将list赋值为undefined就可以使self中的add,remove,fire,fireWith方法停止工作                  // 我认为这里把stack、memory赋值为undefined与否是没有任何关系的                  disable: function() {                      list = stack = memory = undefined;                      return this;                  },                  // Is it disabled?                  disabled: function() {                      return !list;                  },                  // Lock the list in its current state                  // 锁定回调列表                  // 如果(fired !== true || options.memory === false),则视为禁用(disable)                  // 如果(fired === true && options.memory === true),则视为options.once === true                  // 请看实例2                  lock: function() {                      stack = undefined;                      if ( !memory ) {                          self.disable();                      }                      return this;                  },                  // Is it locked?                  locked: function() {                      return !stack;                  },                  // Call all callbacks with the given context and arguments                  fireWith: function( context, args ) {                      // 回调对象未执行过fire 或且 可以执行多次(options.once === false)                      // 如果(fired === true && options.once === true),则不会执行fire操作                      if ( list && ( !fired || stack ) ) {                          args = args || [];                          args = [ context, args.slice ? args.slice() : args ];                          if ( firing ) {                              stack.push( args );                          } else {                              fire( args );                          }                      }                      return this;                  },                  // Call all the callbacks with the given arguments                  fire: function() {                      self.fireWith( this, arguments );                      return this;                  },                  // To know if the callbacks have already been called at least once                  fired: function() {                      return !!fired;                  }              };               return self;      };
三、实例实例1、 处理回调函数中的fire,或fireWidth操作     var cb = jQuery.Callbacks();           var fn1 = function(arg){ console.log( arg + '1' ); };      var fn2 = function(arg){ console.log( arg + '2' ); cb.fire();  };      var fn3 = function(arg){ console.log( arg + '3' ); };           cb.add(fn1, fn2, fn3);           cb.fire('fn'); // 其中回调fn1,fn2,fn3无限制的循环调用           /*      控制台将无限制输出如下:      fn1      fn2      fn3      fn1      fn2      fn3      fn1      fn2      fn3      .      .      .      */  实例2、 锁定(lock)操作各种场景中的用法    var cb1 = jQuery.Callbacks();      var cb2 = jQuery.Callbacks('memory');      var cb3 = jQuery.Callbacks('memory');           var fn1 = function(arg){ console.log( arg + '1' ); };      var fn2 = function(arg){ console.log( arg + '2' ); };      var fn3 = function(arg){ console.log( arg + '3' ); };           // 如果options.memory !== true,锁定操作视为禁用回调对象      cb1.add(fn1);      cb1.lock();      // 以下操作无任何反应      cb1.add(fn2);      cb1.fire('fn');           // 如果fried !== true,锁定操作也视为禁用回调对象      cb2.add(fn1);      cb2.lock();      // 以下操作无任何反应      cb2.add(fn2);      cb2.fire('fn');           // 如果(fired === true && options.memory === true),锁定操作类似控制标志once(options.once === true);      cb3.add(fn1);      cb3.fire('fn'); // fn1,此时fired === true      cb3.lock();     // 像是传入了'once'标志,jQuery.Callbacks('once memory');      cb3.add(fn2);   // fn2      cb3.fire('fn'); // 再次触发,无任何反应      cb3.add(fn3);   // fn3                // 再来看看jQuery.Deferred中的一段源码      var tuples = [          // action, add listener, listener list, final state          [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],          [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],          [ "notify", "progress", jQuery.Callbacks("memory") ]      ];           // Handle state      if ( stateString ) {          list.add(function() {              // state = [ resolved | rejected ]              state = stateString;               // [ reject_list | resolve_list ].disable; progress_list.lock          }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );      }           /*          当执行了tuples中前面两组中任意一个回调对象的fire方法时,后一组回调对象被锁定,          相当于(fired === true && options.memory === true),后一组回调对象实际为执行          jQuery.Callbacks('once memory')生成的回调对象。      */

 

转载于:https://www.cnblogs.com/chris-oil/p/3810944.html

你可能感兴趣的文章
SQL练习之求解填字游戏
查看>>
DOM
查看>>
UIApplication
查看>>
12:Web及MySQL服务异常监测案例
查看>>
数据库性能优化之冗余字段的作用
查看>>
DBA_实践指南系列9_Oracle Erp R12应用补丁AutoPatch/AutoControl/AutoConfig(案例)
查看>>
数据库设计三大范式
查看>>
ionic 字体的导入方法
查看>>
IP路由原理
查看>>
内部类详解
查看>>
洛谷P2726 阶乘 Factorials 数学
查看>>
类加载机制
查看>>
火柴棒等式(2008年NOIP全国联赛提高组)
查看>>
mongodb int型id 自增
查看>>
【转】关于大型网站技术演进的思考(十八)--网站静态化处理—反向代理(10)...
查看>>
Java中的4种代码块
查看>>
Ocelot(七)- 入门
查看>>
生成水杯热气
查看>>
程序员工作心法
查看>>
三个常用的PHP图表类库
查看>>