// 对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. java-信息安全(三)-PBE加密算法

    概述 信息安全基本概念: PBE算法(Password Based Encryption,基于口令加密) PBE PBE算法(Password Based Encryption,基于口令加密)是一种基 ...

  2. 在C++中实现不可继承的类

    逛下bbs,“在C++中实现不可继承的类”,瞒有意思的. class NoInherite { friend class Seal; private: NoInherite(void) {} ~NoI ...

  3. xml文件加密上传和sftp下载解密基本思路

    AES对称加密效率高,对大文件加密适合.RSA非对称加密效率低,更安全,对小文件加密适合. 整理 11:12 2016/8/4 加密:1.xml xml.md5摘要 2.(xml)aes加密 (xml ...

  4. 如何破解银行O2O模式创新

    文/赵志宏 摩 根大通的买房APP,使客户可根据自己的位置选择合适的贷款经理:华道数据提供的卡惠APP,使客户可随时查询自己周围信用卡刷卡打折的商户信息:民生银 行的微信预约叫号功能,使客户根据可自己 ...

  5. .NET工具集合

    工具 (1) 代码分析 .NET Memory Profiler - http://memprofiler.com/ANTS Profiler - http://www.red-gate.com/co ...

  6. IOS端的摇一摇功能

    //微信的摇一摇是怎么实现的~发现原来 ios本身就支持 //在 UIResponder中存在这么一套方法 - (void)motionBegan:(UIEventSubtype)motion wit ...

  7. 01-虚拟软件vmware安装

    什么是虚拟软件: 虚拟原件是一个可以使你在一台机器上同时运行二个或更多Windows.LINUX等系统.它可以模拟一个标准PC环境.这个环境和真实的计算机一样,都有芯片组.CPU.内存.显卡.声卡.网 ...

  8. spring学习之springMVC 返回类型选择 以及 SpringMVC中model,modelMap.request,session取值顺序

    spring mvc处理方法支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void.下面将对具体的一一进行说明: ModelAn ...

  9. The import XXX cannot be resolved

    选择project --> clean后,OK.如果还不行,删掉全部import,然后按alt+/提示,逐个导入.

  10. 【GIS】Cesium GLTF

    cd D:\GISSoft\3DsMax2017\COLLADA2GLTF-v2.1.4-windows-Release-x64 COLLADA2GLTF-bin.exe -f tree05.DAE ...