// 对option的一个缓存,避免每次都需要createOptions,option是创建Callback对象时的传入的参数
// 每个option被存入optionsCache中类似于{memory:true, once:true,...}
var optionsCache = {};

// 将字符串表达转成对象表达,并存在缓存中,一个内部使用的方法。
function createOptions( options ) {
  var object = optionsCache[ options ] = {};
  jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
    object[ flag ] = true;
  });
  return object;
}

/*

* Callback构造函数在构造对象时的一些参数选项


 * once: 确保这个回调列表只执行一次(像一个递延 Deferred).
 * memory: 保持以前的值和将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred).
 * unique: 确保一次只能添加一个回调(不能列表中的重复).
 * stopOnFalse: 当一个回调返回false 时中断调用

*/

// options参数以字符串的形式传入,例如"unique memory",以空格分割,用来控制回调函数的行为
// $.Callbacks是在jQuery内部使用,如为$.ajax,$.Deferred等组件提供基础功能的函数。它也可以用在类似功能的一些组件中,如自己开发的插件。
jQuery.Callbacks = function( options ) {

  options = typeof options === "string" ?
  // 先检测一下cache,如果为空,则把options从字符串的形式转变为Object的形式
  // 通过字符串在optionsCache寻找有没有相应缓存,如果没有则创建一个,有则引用
  ( optionsCache[ options ] || createOptions( options ) ) :
  // 如果是对象则通过jQuery.extend扩展到新对象中赋值给options
  jQuery.extend( {}, options );

  var 
  // 最后一次触发回调时传的参数
  memory,
  // 列表中的函数是否已经回调至少一次
  fired,
  // 列表中的函数是否正在回调中 
  firing,
  // 回调的起点
  firingStart,
  // 回调时的循环结尾
  firingLength,
  // 当前正在回调的函数索引
  firingIndex,
  // 回调函数列表
  list = [],
  // 可重复的回调函数堆栈,用于控制触发回调时的参数列表
  stack = !options.once && [],
  // 触发回调函数列表

  //这里的data参数到底是啥???
  fire = function( data ) {
    //如果参数memory为true,则记录data
    memory = options.memory && data;
    //标记触发回调
    fired = true;
    firingIndex = firingStart || 0;
    firingStart = 0;
    firingLength = list.length;
    //标记正在触发回调
    firing = true;
    for ( ; list && firingIndex < firingLength; firingIndex++ ) {
      // 对当前回调函数在data[0]环境下,传入data[1]参数,并执行,如果返回false,并且设置了stopOnFalse,则将memory设为false并返回。
      if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {

        // 阻止未来可能由于add所产生的回调,因为当memory为true时,add进来的回调函数会立即被触发
        memory = false;

        //由于参数stopOnFalse为true,所以当有回调函数返回值为false时退出循环
        break;
      }
    }
    //标记回调结束
    firing = false;
    //如果列表存在
    if ( list ) {
      //如果堆栈存在
      if ( stack ) {
        //如果堆栈不为空
        if ( stack.length ) {
          //从堆栈头部取出,递归fire。
          fire( stack.shift() );
        }
      //否则,如果有记忆
      } else if ( memory ) {
        //列表清空
        list = [];
      //再否则阻止回调列表中的回调
      } else {
        self.disable();
      }
    }
  },
  //  Callback构造函数返回的对象
  self = {
    // 向回调列表中添加一个回调或回调的集合。
    add: function() {
      if ( list ) {
        // 首先我们存储当前列表长度 主要用于当Callbacks传入memory参数时
        var start = list.length;
        // 在做其它操作之前,先对args做一些处理
        (function add( args ) {
          // 对args传进来的列表的每一个对象执行操作
          jQuery.each( args, function( _, arg ) {
            var type = jQuery.type( arg );
            // 如果是函数
            if ( type === "function" ) {
              // 如果回调离别不允许重复,则看看列表中是否包含,如果不包含则加入列表
              if ( !options.unique || !self.has( arg ) ) {
                list.push( arg );
              }
            // 如果是类数组或对象,则递归调用add方法
            } else if ( arg && arg.length && type !== "string" ) {
              //递归 调用add方法往list中增加回调
              add( arg );
            }
          });
        })( arguments );
        // 如果整个callbacks对象中的callback正在执行时,回调时的循环结尾变成add之后的list长度长度,确保新增加的回调函数会被执行到
        if ( firing ) {
          firingLength = list.length;
        // 如果有memory,我们立刻调用。 这里的memory
        } else if ( memory ) {
          firingStart = start;
          fire( memory );
        }
      }
      return this;
    },
    // 从列表删除回调函数
    remove: function() {
      if ( list ) {
        //继续用jQuery.each,对arguments中的所有参数处理
        jQuery.each( arguments, function( _, arg ) {
          var index;
          //找到arg在列表中的位置
          while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
            //根据得到的位置删除列表中的回调函数
            list.splice( index, 1 );
            if ( firing ) {
              //如果正在回调过程中,则调整循环的索引和长度
              if ( index <= firingLength ) {
                firingLength--;
              }
              if ( index <= firingIndex ) {
                firingIndex--;
              }
            }
          }
        });
      }
      return this;
    },
    // 判断回调函数是否在列表中
    has: function( fn ) {
      return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
    },
    // 从列表中删除所有回调函数
    empty: function() {
      list = [];
      firingLength = 0;
      return this;
    },
    // 禁用回调列表中的回调。禁用之后无法再向列表中加入回调函数
    disable: function() {
      list = stack = memory = undefined;
      return this;
    },
    // 判断列表是否被禁用
    disabled: function() {
      return !list;
    },
    // 锁定列表
    lock: function() {
      stack = undefined;
      if ( !memory ) {
        self.disable();
      }
      return this;
    },
    // 判断列表是否被锁
    locked: function() {
      return !stack;
    },
    // 以给定的上下文和参数调用所有回调函数
    fireWith: function( context, args ) {
      if ( list && ( !fired || stack ) ) {
        args = args || [];
        args = [ context, args.slice ? args.slice() : args ];
        //如果正在回调
        if ( firing ) {
          //将参数推入堆栈,等待当前回调结束再调用
          stack.push( args );
          //否则直接调用
        } else {
          fire( args );
        }
      }
      return this;
    },
    // 以给定的参数调用所有回调函数
    fire: function() {
      self.fireWith( this, arguments );
      return this;
    },
    // 回调函数列表是否至少被调用一次
    fired: function() {
      return !!fired;
    }
  };

  return self;
};

jQuery的回调管理机制的更多相关文章

  1. jQuery的回调管理机制(三)

    jQuery.when()方法是jQuery内部使用回调机制的范例. // 参数为多个方法,这些方法全部执行完成之后执行回调 when: function( subordinate /* , ..., ...

  2. jQuery的回调管理机制(二)

    jQuery.extend({ /*  * deferred对象的一大好处,就是它允许你自由添加多个回调函数. * $.ajax("test.html")   .done(func ...

  3. ARC内存管理机制详解

    ARC在OC里面个人感觉又是一个高大上的牛词,在前面Objective-C中的内存管理部分提到了ARC内存管理机制,ARC是Automatic Reference Counting---自动引用计数. ...

  4. Android包管理机制(二)PackageInstaller安装APK

    前言 在本系列上一篇文章Android包管理机制(一)PackageInstaller的初始化中我们学习了PackageInstaller是如何初始化的,这一篇文章我们接着学习PackageInsta ...

  5. Linux中断管理 (1)Linux中断管理机制

    目录: <Linux中断管理> <Linux中断管理 (1)Linux中断管理机制> <Linux中断管理 (2)软中断和tasklet> <Linux中断管 ...

  6. untiy3d action管理机制的编写

    使用unity3d对于一些可视化强迫者来说,是一个不错的选择,但unity3d没有cocos2d的action管理机制,比如cocos2dx的CCMoveTo,CCScale等action,所以笔者通 ...

  7. Android开发——Android 6.0权限管理机制详解

    .Android 6.0运行时主动请求权限 3.1  检测和申请权限 下面的例子介绍上面列出的读写SD卡的使用例子,可以使用以下的方式解决: public boolean isGrantExterna ...

  8. Linux中断管理 (1)Linux中断管理机制【转】

    转自:https://www.cnblogs.com/arnoldlu/p/8659981.html 目录: <Linux中断管理> <Linux中断管理 (1)Linux中断管理机 ...

  9. Android中的内存管理机制以及正确的使用方式

    概述 从操作系统的角度来说,内存就是一块数据存储区域,属于可被操作系统调度的资源.现代多任务(进程)的操作系统中,内存管理尤为重要,操作系统需要为每一个进程合理的分配内存资源,所以可以从两方面来理解操 ...

随机推荐

  1. C# EF Code First Migrations数据库迁移

    1.EF Code First创建数据库 新建控制台应用程序Portal,通过程序包管理器控制台添加EntityFramework. 在程序包管理器控制台中执行以下语句,安装EntityFramewo ...

  2. 一款标注颜色,距离的小软件 markman

    长度标记   坐标和矩形标记   色值标记   文字标记   长度自动测量   标记拖拽删除   支持多种图片格式 支持PSD(需用最大兼容保存).PNG.BMP.JPG格式 设计稿自动刷新 在标注的 ...

  3. Java Decompiler Plugin For Eclipse IDE

    1. 下载JAD , 1.5.8版本的jad在 http://www.softpedia.com/progDownload/JAD-Download-85911.html 将展开后的jad.exe放到 ...

  4. Java注解Annotation学习笔记

    一.自定义注解 1. 使用关键字 @interface 2. 默认情况下,注解可以修饰 类.方法.接口等. 3. 如下为一个基本的注解例子: //注解中的成员变量非常像定义接口 public @int ...

  5. mac ssh中文乱码解决

    网上有如下解决法,至少我没有成功过: vim ~/.bash_profile export LC_ALL='zh_CN.utf8' 来源:http://www.liuhuadong.com/archi ...

  6. [转]POI实现读写Excel2007完整示例

    http://blog.csdn.net/little_stars/article/details/8210532 流程:(跟jxl相似,只是读取逻辑有点不同) 跟jxl的两处主要区别: 1.读取和写 ...

  7. 移动端网页使用flexible.js加入百度联盟广告样式不一致问题解决

    flexible.js是淘宝推出的一款移动端手机自适应的库,源码内容很简洁,当网页使用了该库之后,页面会在head中加入对应的页面响应式的meta标签. 当使用flexible.js的时候,引入百度联 ...

  8. Yslow-23条军规

    YslowYahoo发布的一款基于FireFox的插件,主要是为了提高网页性能而设计的,下面是它提倡了23条规则,还是很不错的,分享一下: 1.减少HTTP请求次数 合并图片.CSS.JS,改进首次访 ...

  9. Matlab 随机数字

    1.随机生成仅仅有0.1元素的矩阵(m行n列) A=round(rand(m,n)) 2.随机生成每行有若干个0,1元素的矩阵,比方每行仅仅有2个元素为1,其它元素为0 A=zeros(4,5) fo ...

  10. 双调旅行商问题 (Bitonic TSP)

    问题描写叙述: 上述问题能够使用动态规划的方法来解决. 以下是解决思路的详细介绍: 1. 最优子结构: 如果d[i][j]表示从起点1出发到达i及j两个顶点的最短路程之和. 为此能够如果K为此段路程上 ...