// 对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. JSON数组,JSON对象,数组的区别与基本操作整理

    JSON 语法规则 JSON 语法是 javascript 对象表示语法的子集. 数据在名称/值对中 数据由逗号分隔 花括号保存对象 方括号保存数组 JSON 名称/值对 JSON 数据的书写格式是: ...

  2. UIStatusBarStyle PreferredStatusBarStyle does not work on iOS 7

    Q: In my iPhone application built with Xcode 5 for iOS 7 I set UIViewControllerBasedStatusBarAppeara ...

  3. 删除mac系统win10启动选择项

    打开终端输入:diskutil list找到EFI这个分区,挂载EFI分区diskutil mount /dev/disk0s1 回到Finder 删除除apple之外的两个文件夹就可以了(删除win ...

  4. mysql Communication link failure, message from server: "Can't get hostname for your address"

    在连接mysql jdbc时候,抛出了 com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Communicat ...

  5. Sql Server Snapshot和mysql MVCC

    mysql 在一个事务A中插入一条数据 在B事务中查询到的还是以前的数据,可以select *from table,不被锁住   Sql Server 默认级别 读已提交 所以A事务在 X表插入数据, ...

  6. UNIX环境编程学习笔记(27)——多线程编程(二):控制线程属性

    lienhua342014-11-09 1 线程属性概括 POSIX 线程的主要属性包括 scope 属性.detach 属性.堆栈地址.堆栈大小.优先级.在头文件 pthread.h 中定义了结构体 ...

  7. 在web项目中集成pdf.js的默认查看器

    pdf.jsMozilla开源的一项用于在HTML5平台上显示pdf文档的技术,Mozilla自己的Firefox浏览器也用了pdf.js来预览pdf,可见应该是一个比较成熟稳定的方案(btw,chr ...

  8. Windows 下 绿化 Oracle

    既然Oracle在非windows平台上可以很“绿色”的执行,那么在windows平台上有无可能呢? 答案是“Yes-No” 基本上,除了监听器(lisentner)这个异类外,Oracle实例完全可 ...

  9. 英文版Ubuntu18.10安装搜狗输入法过程(图文并茂,亲自尝试!)

    英文版Ubuntu18.10安装搜狗输入法过程 过程比较艰辛,折腾了好长的时间,不过最终还是装好了,特记录一下! 首先去搜狗输入法网址下载Linux版本:https://pinyin.sogou.co ...

  10. 消息中间件的意义和应用场景 (activeMq)

    消息中间件一般两个功能,解耦和异步处理,分别举个例子吧 解耦合:比如我们做一个微博产品中的好友系统,就很需要使用消息中间件当我们添加一个关注的时候, 涉及以下几个子系统 推荐系统,需要根据你关注的人给 ...