JQuery并没有简单的使用一个Array来存储回调函数,而是通过JQuery.Callbacks(options)返回一个self对象,此对象能够动态的add,remove和fire回调函数队列.此函数须要说明的是options參数,它是一个string,这个string由四个參数随意组合而成

options:
once:回调函数仅仅运行一次
memory:调用add时触发回调函数使用fire时传入的參数.
unique:回调函数仅仅能被加入到队列一次
stopOnFlase:有一个回调函数返回false,停止运行以下的回调函数.

JQuery.Callbacks(options)内的变量依据不同传參有不同的意思,这些变量由闭包引用扩大其生命周期,以在外部改变其值,下面进行说明
memory:假设options不包括'memory'字符串,memeory为false,其它为上一次fire(args)的參数.
fired:回调函数列表是否被触发过.
firing:回调函数列表是否正在被触发.为何要这个參数呢?由于假设在回调函数内调用了add,remove,fire 方法时会改变回调函数列表的状态(開始触发的位置,触发长度等等,这些是控制要运行回调函数列表中哪些函数的标志),此时其回调函数函数列表正在处于触发中,必须能够及时反映(修正触发位置,触发长度等等)回调函数列表的变化,以便在回调函数运行的循环中下一次迭代能够正确的触发回调函数列表.
firingStart:回调函数列表运行的開始位置.
firingLength:再运行几个回调函数
firingIndex:正在触发的函数在回调函数列表中的位置
list:存放回调函数
stack:假设在回调函数中调用fire或fireWith方法,将fire或fireWith传入的上下文和參数先入栈,稍后再用栈中的參数来作为參数运行回调函数中调用的fire或fireWidth。

注:不解的是走到stack.push分支时是在回调函数里调用fire或fireWidth,但这样会导致死循环,这个stack究竟怎么回事?还请明确的同学告知我一下。!

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 // Last fire value (for non-forgettable lists)
memory,
// Flag to know if list was already fired
fired,
// Flag to know if list is currently firing
firing,
// First callback to fire (used internally by add and fireWith)
firingStart,
// End of the loop when firing
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
// Actual callback list
list = [],
// Stack of fire calls for repeatable lists
// 假设没有配置once,stack为数组
// stack存放反复时fire參数
stack = !options.once && [],
// Fire callbacks
fire = function( data ) {
//memory 为false或fire时传入的參数
memory = options.memory && data;
fired = true;
//当前触发位置
firingIndex = firingStart || 0;
//触发回调函数队列的起始位置
firingStart = 0;
//待触发的回调函数队列长度
firingLength = list.length;
//正在触发标识设置为true
//仅仅有在Callbacks的回调函数里此值才会为true
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
//若配置了stopOnFalse当回调函数队列中函数返回值为false
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
//memory设置为false
memory = false; // To prevent further calls using add
//不再运行后面的回调函数
break;
}
}
//回调函数运行结束
firing = false;
if ( list ) {
//未配置once
if ( stack ) {
//运行在回调函数里调用的fire
if ( stack.length ) {
fire( stack.shift() );
}
//清空回调函数列表,但此callbacks仍可用
} else if ( memory ) {
list = [];
//让闭包的那些引用能够销毁
} 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
// 将加入回调函数之前将当前callbacks的长度设为运行開始
var start = list.length;
( function add( args ) {
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
if ( type === "function" ) {
// 假设未设置unique或者回调函数列表里没有此函数
// 在回调函数队列里加上此函数
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
// 假设arg是个类数组,再递归add
} 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方法会走此分支
if ( firing ) {
//立马反应要触发回调函数长度的改变
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
} else if ( memory ) {
//改变触发的開始位置为未加入前的回调队列长度
firingStart = start;
fire( memory );
}
}
return this;
},
// Remove a callback from the list
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
//arg所在list的索引
var index;
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
//删除
list.splice( index, 1 );
// Handle firing indexes
//假设在回调函数里调用remove
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
empty: function() {
list = [];
firingLength = 0;
return this;
},
// Have the list do nothing anymore
//是回调函数列表功能全然失效
disable: function() {
list = stack = memory = undefined;
return this;
},
// Is it disabled? disabled: function() {
return !list;
},
// Lock the list in its current state
//锁住当前的状态,不会运行在回调函数里调用fire或fireWidth
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 ) {
//没有被触发或者能够反复触发
if ( list && ( !fired || stack ) ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
//?事实上这里有一点不解,走到这个分
//是在在回调函数里调通fire或fireWidth,但这样会导致死循环
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;
};

JQuery日记6.9 Promise/A之Callbacks的更多相关文章

  1. javascript异步代码的回调地狱以及JQuery.deferred提供的promise解决方式

    我们先来看一下编写AJAX编码常常遇到的几个问题: 1.因为AJAX是异步的,全部依赖AJAX返回结果的代码必需写在AJAX回调函数中.这就不可避免地形成了嵌套.ajax等异步操作越多,嵌套层次就会越 ...

  2. jquery源码分析(四)——回调对象 Callbacks

    借用百度百科来说明下回调函数: 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数.回调函数不是由该 ...

  3. JQuery日记6.7 Javascript异步模型(二)

    异步模型看起来非常美,但事实上它也是有天生缺陷的.看以下代码 try { setTimeout( function(){ throw new Error( '你抓不到我的!' ); }, 100); ...

  4. JQuery日记_5.13 Sizzle选择器(六)选择器的效率

        当选择表达式不符合高速匹配(id,tag,class)和原生QSA不可用或返回错误时,将调用select(selector, context, results, seed)方法,此方法迭代DO ...

  5. JQuery日记6.5 Javascript异步模式(一)

    理解力JQuery前实现异步队列,有必要理解javascript异步模式. Javascript异步其实并不严重格异步感,js使某些片段异步方式在将来运行,流不必等待继续向下进行. 在多线程的语言中最 ...

  6. JQuery日记 5.11 Sizzle选择器(五)

    //设置当前document和document相应的变量和方法 setDocument = Sizzle.setDocument = function( node ) { var hasCompare ...

  7. JQuery日记 5.31 JQuery对象的生成

    JQuery对象的生成 1 selector为不论什么可转换false的空值   返回空JQuery对象 2 selector为字符串   2.1 selector为html字符串或有id属性的标签 ...

  8. JQuery日记_5.14 Sizzle选择器(七)

    上篇说道,tokenize方法会把selector切割成一个个selector逻辑单元(如div>a是三个逻辑单元 'div','>','a')并为之片段赋予相应类型的过滤函数. for ...

  9. Jquery AJAX如何使用Promise/Deferred实现顺序执行?

    有的时候有我有N个AJAX请求,第下个请求可能要依赖上个请求的返回值, 可以用 $.ajax("test1.php").then(function(data) { // data ...

随机推荐

  1. Inno Setup使用上的几个问题 (转)

    Inno Setup使用上的几个问题: [问题一:Inno Setup 执行REG文件代码?][Run]Filename: "{win}\regedit.exe";Paramete ...

  2. haproxy image跳转 haproxy匹配 匹配到了就停止,不会继续往下匹配

    <pre name="code" class="html">/***第一种 nginx 配置: location / { root /var/www ...

  3. 一封推荐信——android培训机构

    我,男,23岁,即将毕业的大四学生,就读于天津一所二本院校,计算机科学与技术专业.大一期间,进入新校园,和同学到各个宿舍推销陶瓷杯,国美电器饮水机促销员,组团蹬车游市区,不断地去探索.尝试,追求内心向 ...

  4. svn unable to connect to a repository at url 执行上下文错误 不能访问SVN服务器问题

    1.检查visual SVN server 本地仓库服务是否正常启动 如果是SVN server service的的问题情形: (1).使用browser打开仓库位置,结果连接打不开. (2).查看s ...

  5. linux之SQL语句简明教程---Alias

    接下来,我们讨论 alias (别名) 在 SQL 上的用处.最常用到的别名有两种: 栏位别名及表格别名. 简单地来说,栏位别名的目的是为了让 SQL 产生的结果易读.在之前的例子中,每当我们有营业额 ...

  6. Dictionary到List转换中的性能问题 转

    本文来自:http://www.cnblogs.com/353373440qq/p/3488367.html 在应用泛型中,我们经常使用Dictionary,经常会用到Dictionary到List的 ...

  7. 高性能浏览器网络(High Performance Browser Networking) 第二章

    第2章 TCP篇 互联网的核心是两个协议,IP和TCP. IP也叫Internet协议,提供主机到主机的路由和寻址:TCP,传输控制协议,在不可靠的传输通道上提供一个可靠的网络抽象.TCP / IP协 ...

  8. Appium Android Bootstrap控制源代码的分析AndroidElement

    通过上一篇文章中<Appium Android Bootstrap源代码分析之简单介绍>我们对bootstrap的定义以及其在appium和uiautomator处于一个什么样的位置有了一 ...

  9. Revisit-after元标签是什么,SEO的作用是什么

    很多网站都使用了revisit-after的问题,关于revisit-after这个标签是什么,revisit-after元标签的作用究竟是什么呢,应该如何使用revisit-after元标签呢? 这 ...

  10. Android Service组件(1)

    android service 和其他服务一样,并没有实际运行的界面,它运行在android 后台.一般通过service为应用程序提供服务(比如,从Internet下载文件,控制音乐播放器等).Se ...