callbacks是jquery的核心之一。

语法如下:

jQuery.Callbacks( flags )   flags 类型: String 一个用空格标记分隔的标志可选列表,用来改变回调列表中的行为。

once: 确保这个回调列表只执行( .fire() )一次(像一个递延 Deferred).

memory: 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred).

unique: 确保一次只能添加一个回调(所以在列表中没有重复的回调).

stopOnFalse: 当一个回调返回false 时中断调用

使用案例:

function fn1(val) {
console.log('fn1 says ' + val);
}
function fn2(val) {
console.log('fn2 says ' + val);
return false;
}
var cbs = $.Callbacks('once memory');
cbs.add(fn1);
//第一次fire会缓存传入的参数
cbs.fire('foo'); //fn1 says foo
//fire过一次之后,以后的add都会自动调用fire,传入的参数是上次fire传入的'foo'
cbs.add(fn2); //fn2 says foo
 function fn1(val) {
console.log('fn1 says ' + val);
}
function fn2(val) {
console.log('fn2 says ' + val);
return false;
}
var cbs = $.Callbacks('stopOnFalse');
cbs.add(fn2)
cbs.add(fn1);
cbs.fire('foo'); //fn2 says foo
function fn1(val) {
console.log('fn1 says ' + val);
}
function fn2(val) {
console.log('fn2 says ' + val);
return false;
}
var cbs = $.Callbacks('once');
cbs.add(fn2)
cbs.add(fn1);
cbs.fire('foo'); //fn2 says foo fn1 says foo cbs.add(fn2);
cbs.fire("bar"); //不输出
function fn1(val) {
console.log('fn1 says ' + val);
}
function fn2(val) {
console.log('fn2 says ' + val);
return false;
}
var cbs = $.Callbacks('stopOnFalse');
cbs.add(fn2)
cbs.add(fn1);
cbs.fire('foo'); //fn2 says foo cbs.add(fn2);
cbs.fire("bar"); //fn2 says bar

源码分析:

// String to Object options format cache
var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache
//20170618 huanhua 参数options="once memory"
// 返回结果: optionsCache[once memory]={once:true,memory:true}
function createOptions( options ) {
var object = optionsCache[options] = {};
//20170618 huanhua 前面已经定义正则 core_rnotwhite= /\S+/g , options.match( core_rnotwhite )返回数组["once","memory"]类似这种
jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true;
});
return object;
} /*
* Create a callback list using the following parameters:
*
* options: an optional list of space-separated options that will change how
* the callback list behaves or a more traditional option object
*
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
*
* Possible options:
*
* once: will ensure the callback list can only be fired once (like a Deferred)
*
* memory: will keep track of previous values and will call any callback added
* after the list has been fired right away with the latest "memorized"
* values (like a Deferred)
*
* unique: will ensure a callback can only be added once (no duplicate in the list)
*
* stopOnFalse: interrupt callings when a callback returns false
*
*/
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 // Flag to know if list is currently firing
firing,
// Last fire value (for non-forgettable lists)
//20170618 huanhua 存储的是最后一次 fire传递的参数值
memory,
// Flag to know if list was already fired
fired,
// End of the loop when firing
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
// First callback to fire (used internally by add and fireWith)
firingStart,
// Actual callback list
// 20170618 huanhua 回调函数的存储列表
list = [],
// Stack of fire calls for repeatable lists
// 20170618 huanhua 回调函数不是只执行一次的时候,用 stack保存正在执行回调的时候,再次请求执行的 fire()中传入的参数
stack = !options.once && [],
// Fire callbacks
fire = function (data) {//20170618 data是一个数组[context,arg],第一个是执行的上下午,第二个是真正的参数
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for (; list && firingIndex < firingLength; firingIndex++) {
//20170618 huanhua 判断
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
// 阻止未来可能由于add所产生的回调,add中有这段代码
//else if ( memory ) {
// firingStart = start; //20170618 huanhua 从列表最新添加的回调函数位置开始执行
// fire( memory ); //20170618 huanhua 立即执行回调函数
//}
memory = false; // To prevent further calls using add
break;//由于参数stopOnFalse为true,所以当有回调函数返回值为false时退出循环
}
}
firing = false;
if (list) {
//20170618 huanhua 处理正在执行回调中,再次触发的 fire(),也就是同时第一个触发的 fire还没执行完,紧接着多次执行了 fire
//在 fireWith 中有这个代码:if ( firing ) {stack.push(args);} 
     if (stack) {//20170619 huanhua  只要没有执行 disable和lock(stack=undefined) 或者 options不包含once 就会执行这里
if ( stack.length ) {
fire( stack.shift() );
}
} else if (memory) { //20170619 huanhua 只有在 options = "once memory" 的时候才会执行这里
list = [];
} else {//20170619 huanhua options 等于 ["once","once stopOnFalse","once union"]中的任何一个的时候才会执行这里
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
var start = list.length;
(function add(args) {
//20170618 huanhua add(fun1,fun2,fun3)
jQuery.each( args, function( _, arg ) {
var type = jQuery.type(arg);
//20170618 huanhua 判断是函数
if (type === "function") {
//20170618 huanhua !options.unique判断 当$.Callbacks('unique')时,保证列表里面不会出现重复的回调
//!self.has( arg )只要不列表里不存在,就添加到列表
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
//20170618 huanhua 判断传递的参数是类似数组的对象,或者就是数组
// add({leng:2,0:fun1,1:fun2},{leng:1,0:fun3});
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
//20170618 huanhua 当fire执行的时间需要比较长的时候,我们在执行 add的时候,fire也在执行,add执行完后,fire还没执行完,为了防止新加的不执行,
//所以重新赋值了需要执行的回调函数个数 firingLength = list.length;
if ( firing ) {
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
//20170618 huanhuaa $.CallBacks("memory"),并且还调用了 fire
} else if ( memory ) {
firingStart = start; //20170618 huanhua 从列表最新添加的回调函数位置开始执行
fire( memory ); //20170618 huanhua 立即执行回调函数
}
}
return this;
},
// Remove a callback from the list
//20170618 huanhua 删除指定的回调函数
remove: function () {
//20170618 huanhua 判断回调函数列表是有效的
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
//20170618 huanhua index = jQuery.inArray( arg, list, index )获取需要被删除的回调函数在列表中的位置
while ((index = jQuery.inArray(arg, list, index)) > -1) {
//20170618 huanhua 删除回调函数
list.splice( index, 1 );
// Handle firing indexes
//20170618 huanhua 如果删除的时候在执行 fire()
if (firing) {
//20170618 huanhua 当被删除的回调函数的位置 <= 回调函数列表总长度的时候,删除了一个回调函数,所以 firingLength 要减一个了
if ( index <= firingLength ) {
firingLength--;
}
//20170618 huanhua 当被删除的回调函数的位置 <= 执行到的回调函数的位置时候,删除了一个回调函数,所以 firingIndex 要减一个了
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) {//回调函数是否在列表中.
//20170616 huanhua !!( list && list.length ) 如果执行的 has("")/has(undefined)等等,如果 执行了 def.disable(),list就等于undefined了
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
// Remove all callbacks from the list
//20170618 huanhua 清空回调函数列表
empty: function() {
list = [];
return this;
},
// Have the list do nothing anymore
//20170618 huanhua 禁用回调列表中的回调。
disable: function() {
list = stack = memory = undefined;
return this;
},
// Is it disabled?
disabled: function() {
return !list;
},
// Lock the list in its current state
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 ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
if (list && (!fired || stack)) {
//20170618 huanhua 如果正在执行回调函数
//将参数推入堆栈,等待当前回调结束再调用
if ( firing ) {
stack.push(args);
//20170618 huanhua 如果当前不是正在执行回调函数,就直接执行
} 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中的callbacks之我见的更多相关文章

  1. 深入jQuery中的Callbacks()

    引入 初看Callbacks函数很不起眼,但仔细一瞅,发现Callbacks函数是构建jQuery大厦的无比重要的一个基石.jQuery中几乎所有有关异步的操作都会用到Callbacks函数. 为什么 ...

  2. 深入理解jQuery中的Deferred

    引入 1  在开发的过程中,我们经常遇到某些耗时很长的javascript操作,并且伴随着大量的异步. 2  比如我们有一个ajax的操作,这个ajax从发出请求到接收响应需要5秒,在这5秒内我们可以 ...

  3. 读jQuery源码 - Callbacks

    代码的本质突出顺序.有序这一概念,尤其在javascript——毕竟javascript是单线程引擎. javascript拥有函数式编程的特性,而又因为javascript单线程引擎,我们的函数总是 ...

  4. JQuery中的回调对象

    JQuery中的回调对象 回调对象(Callbacks object)模块是JQuery中的一个很基础的模块,很多其他的模块(比如Deferred.Ajax等)都依赖于Callbacks模块的实现.本 ...

  5. js便签笔记(6)——jQuery中的ready()事件为何需要那么多代码?

    前言: ready()事件的应用,是大家再熟悉不过的了,学jQuery的第一步,最最常见的代码: jQuery(document).ready(function () { }); jQuery(fun ...

  6. jQuery源码分析-jQuery中的循环技巧

    作者:nuysoft/JS攻城师/高云 QQ:47214707 EMail:nuysoft@gmail.com 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 前记:本文收集了jQuery ...

  7. 详解jQuery中 .bind() vs .live() vs .delegate() vs .on() 的区别

    转载自:http://zhuzhichao.com/2013/12/differences-between-jquery-bind-vs-live/ 我见过很多开发者很困惑关于jQuery中的.bin ...

  8. Jquery中.ajax和.post详解

    之前写过一篇<.NET MVC 异步提交和返回参数> ,里面有一些ajax的内容,但是不深入,这次详细剖析下jquery中$.ajax的用法. 首先,上代码: jquery-1.5.1 $ ...

  9. jquery中的$.ajax()的源码分析

    针对获取到location.href的兼容代码: try { ajaxLocation = location.href; } catch( e ) { // Use the href attribut ...

随机推荐

  1. angular.copy(source, destination)

    angular.copy(source, destination)只有1个参数,返回source的深拷贝有2个参数source的深拷贝复制给destination

  2. cocos2dx自定义事件类封装

    GameEvent.h: #pragma once #include "cocos2d.h" USING_NS_CC; class GameEvent { public: //封装 ...

  3. SpringCloud分布式配置中心所遇问题

    1.如果更换了获取配置文件的名称一定要在客户端配置文件的name更改2.不建议服务器设置下拉到git仓库的配置文件地址,可能导致更换地址之后无法获取到新更改的

  4. XML一

    HTML(HyperText Markup Language),即超文本标记语言,是用于描述网页文档的一种描述标记语言.                                  而XML(E ...

  5. build to win读后感

    在软件开发的过程中,不能盲目去show自己的成果,而是要大量考虑别人的意见,在广范围的撒网之后,收集意见,最后在一锤定音. 还有就是,要懂得团队合作,例如,本文介绍了一个事例,作者的团队与科研团队合作 ...

  6. java初始重点语法

    第三章 if基本语法: if(条件){// 表达式 // 代码块 } eg: int a = 10; if(a > 1){ System.out.println("内容"); ...

  7. Linux服务安装配置总结

  8. 安装win10 1703版本操作系统

    1.使用UltraISO 全功能单文件 9.5.3.2900刻录工具,刻录iso文件到U盘里 2.默认刻录之后,U盘分区格式变成了fat32,而fat32单个文件无法超过4GB 而1703版本里面的i ...

  9. python实现Content-Type类型为application/x-www-form-urlencoded发送POST请求

    周日晚上接到公司的电话需要通过新榜的接口拿下微博热搜数据,拿到接口文档看了下很简单的一个post请求,主要根据时间段来获取热搜数据. 在实际编码的过程中经常遇到header的Content-Type的 ...

  10. tomcat接口调用时延开关

    项目中有些页面时延不稳定,需要看每次接口调用时延,怎么看,有两种方法:一种是直接去catalina.out日志中看,一种是直接去localhost_access_log日志中看,第一种需要在代码中实现 ...