// 对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. POJ 3481 Double Queue(STL)

    题意  模拟银行的排队系统  有三种操作  1-加入优先级为p 编号为k的人到队列  2-服务当前优先级最大的   3-服务当前优先级最小的  0-退出系统 能够用stl中的map   由于map本身 ...

  2. 利用neon技术对矩阵旋转进行加速(2)

    上次介绍的是顺时针旋转90度,最近用到了180度和270度,在这里记录一下. 1.利用neon技术将矩阵顺时针旋转180度: 顺时针旋转180度比顺时针旋转90度容易很多,如下图 A1 A2 A3 A ...

  3. Gridview中的选择、删除、编辑、更新、取消留着备用。

    后台程序: public partial class tw2 : System.Web.UI.Page{    protected void Page_Load(object sender, Even ...

  4. 16 go操作Mysql

    mysql模块下载 mysql模块我们从github上下载,地址为:www.github.com/go-sql-driver/mysql go get "github.com/go-sql- ...

  5. Java8比较器,如何对 List 排序

    首页 所有文章 资讯 Web 架构 基础技术 书籍 教程 Java小组 工具资源 Java 8新特性终极指南 2014/06/20 | 分类: 基础技术 | 3 条评论 | 标签: java8 分享到 ...

  6. jquery.fileupload插件 ie9下不支持上传

    根据https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support The following browsers support ...

  7. Linux中什么是块设备 及 lsblk命令的使用

    Linux中I/O设备分为两类:字符设备和块设备.两种设备本身没有严格限制,但是,基于不同的功能进行了分类.(1)字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取.相反,此类设备支 ...

  8. 网站性能测试指标(QPS,TPS,吞吐量,响应时间)详解

    转载:http://www.51testing.com/html/16/n-3723016.html   常用的网站性能测试指标有:吞吐量.并发数.响应时间.性能计数器等. 并发数 并发数是指系统同时 ...

  9. PHP缓存机制详解

    一,PHP缓存机制详解 我们可以使用PHP自带的缓存机制来完成页面静态化,但是仅靠PHP自身的缓存机制并不能完美的解决页面静态化,往往需要和其他静态化技术(通常是伪静态技术)结合使用. output ...

  10. 【能力提升】SQL Server常见问题介绍及高速解决建议

    前言 本文旨在帮助SQL Server数据库的使用人员了解常见的问题.及高速解决这些问题.这些问题是数据库的常规管理问题,对于非常多对数据库没有深入了解的朋友提供一个大概的常见问题框架. 以下一些问题 ...