Callbacks : 对函数的统一管理

Callbacks的options参数接受4个属性,分别是
once : 只执行一次
momery : 记忆
stopOnFalse : 强制退出循环
unique : 唯一

暂时先不管4个属性有什么意思,我们看代码开始部分对options做了处理,如果options是字符串则调用createOptions方法转成json
比如:var cb = $.Callbacks('once momery');
转换为:{once:true,momery:true}
这里就不贴createOptions的源码了,大家可以自己去看一下。

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 );

处理完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
stack = !options.once && [],
// Fire callbacks
fire = function( data ) { ..................
},
self = {
add: function() { ..............
},
remove: function() { ................ },
has: function( fn ) { ..................
},
empty: function() { ..................
},
disable: function() { ..................
},
disabled: function() { ................
},
lock: function() { ..............
},
locked: function() { ..............
},
fireWith: function( context, args ) { ...................
},
fire: function() { ................
},
fired: function() { ................
}
};

最后Callbacks返回了self对象,所以在self上面定义的仅供Callbacks内部使用。

内部方法中有2个比较重要,一个是list,所有add添加的函数都存放在list中,另一个是fire方法,它实现了触发list中的函数的具体逻辑。稍后我会重点讲一下它。

接下来我们分析一下self方法:

add : 添加函数
remove : 删除函数
has : 检测list中是否有相同的函数
empty : 情空list
disable : 禁止所有操作 list = stack = memory = undefined
disabled : list是否可用
lock : 锁
locked :锁是否可用
fireWith : 为触发list中的函数做预处理,最终调用fire方法
fire : 调用fireWith方法
fired : fire方法是否运行过

通过属性的名称不难发现,disabled、locked、fired这3个方法返回的是状态,代码也比较简单,不多做解释相信大家也能看懂。

disabled: function() {
return !list;
},
locked: function() {
return !stack;
},
fired: function() {
return !!fired;
}

fire方法也比较简单,它调用了fireWith后返回自身,方便链式调用。

fire: function() {
self.fireWith( this, arguments );
return this;
},

fireWith方法也不算太复杂,它有2个参数:
context : 执行上下文
              我们看fire方法:self.fireWith( this, arguments );
              是不是就很清楚啦,context就是self对象,args就是arguments
args : arguments

然后对args做了处理。把context与args合并在一个数组中。有经验的朋友应该不难看出,这种形式的数组是给apply用的,没错,私有的fire方法中会用到它。

if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {

接着往下看,if ( list && ( !fired || stack ) )  这个判断主要是为了once也就是只fire一次。

fireWith: function( context, args ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
if ( list && ( !fired || stack ) ) {
if ( firing ) {
stack.push( args );
} else {
fire( args );
}
}
return this;
},

我对这个判断语句拆分一下,方便理解,list就不说了,主要说后面2个:
首次执行fireWith,因为fired是undefined,在这里取反所以为真,确保fire方法至少执行一次,然后在私有fire方法中赋值为true,下一次再执行到这里取反,则为假。

fire = function( data ) {
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0; ......................

看到这里相信大家明白了,要实现once必须stack为假才可以。

stack = !options.once && [],

没有设置options.once,取反为真,则stack为空数组,否则stack等于false

接下去又是一个判断:
if ( firing ) {
          stack.push( args );
} else {
          fire( args );
}

firing是为了避免死循环,当循环内需要执行的函数还没走完,则stack.push( args );

firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
memory = false; // To prevent further calls using add
break;
}
}
firing = false;

当循环走完,检测到stack有长度则再调用fire

if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}

再看add方法,先是一个函数自执行,接收的参数是arguments,然后遍历arguments,判断如果是函数则list.push(arg),否则调用自己add(arg),由此可以看出,add方法不仅可以传一个函数,还能多个函数逗号隔开,如:cb.add(fn1,fn2);

add: function() {
if ( list ) {
// First, we save the current length
var start = list.length;
(function add( args ) {
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
if ( type === "function" ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
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;
},

else if ( memory )  当有记忆功能的时候执行,firingStart = start把循环的起始值设为当前数组的长度值,然后调用fire则只会触发当前添加的函数

私有方法fire定义了索引值、起始值、长度,就开始循环,如果触发的函数返回false,并且options.stopOnFalse为true,则终止循环。

fire = function( data ) {
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
memory = false; // To prevent further calls using add
break;
}
}
firing = false;
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
},

读了一遍源码后,我们应该对使用Callbacks非常熟悉了:

//once
var cb = $.Callbacks('once');
cb.add(function(){
alert('a');
});
cb.add(function(){
alert('b');
});
cb.fire();//弹出a,b
cb.fire();//不执行 //memory
var cb = $.Callbacks('memory');
cb.add(function(){
alert('a');
});
cb.add(function(){
alert('b');
});
cb.fire();//弹出a,b cb.add(function(){ //弹出c
alert('c');
}); //once memory
var cb = $.Callbacks('once memory');
cb.add(function(){
alert('a');
});
cb.add(function(){
alert('b');
});
cb.fire();//弹出a,b cb.add(function(){ //弹出c
alert('c');
});
cb.fire(); //不执行 //add方法多个参数逗号隔开
var cb = $.Callbacks();
cb.add(function(){
alert('a');
},function(){
alert('b');
}); cb.fire(); //弹出a,b //stopOnFalse
var cb = $.Callbacks('stopOnFalse');
cb.add(function(){
alert('a');
return false;
},function(){
alert('b');
}); cb.fire();//弹出a //lock()
var cb = $.Callbacks('memory');
cb.add(function(){
alert('a');
}); cb.fire();//弹出a
cb.lock();//锁住fire() cb.add(function(){ //弹出b
alert('b');
});
cb.fire();//不执行 //remove()
var cb = $.Callbacks();
var fn1 = function(){
alert('a');
};
var fn2 = function(){
alert('b');
};
cb.add(fn1);
cb.add(fn2);
cb.fire(); //弹出a,b cb.remove(fn1,fn2);
cb.fire();//不执行

jquery的回调对象Callbacks详解的更多相关文章

  1. jQuery的deferred对象使用详解——实现ajax线性请求数据

    最近遇到一个ajax请求数据的问题 ,就是想要请求3个不同的接口,然后请求完毕后对数据进行操作,主要问题就是不知道这3个请求誰先返回来,或者是在进行操作的时候不能保证数据都已经回来,首先想到能完成的就 ...

  2. jQuery的deferred对象使用详解

    转自: https://www.cnblogs.com/PengLee/p/5657101.html jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本.每个版本都会引入一些新功能. ...

  3. jQuery的deferred对象使用详解【转载】

    一.什么是deferred对象? 开发网站的过程中,我们经常遇到某些耗时很长的javascript操作.其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们 ...

  4. jquery源码解析:jQuery静态属性对象support详解

    jQuery.support是用功能检测的方法来检测浏览器是否支持某些功能.针对jQuery内部使用. 我们先来看一些源码: jQuery.support = (function( support ) ...

  5. javascript event(事件对象)详解

    javascript event(事件对象)详解   1. 事件对象     1. 事件对象 Event 对象代表事件的状态,比如事件在其中发生的元素.键盘按键的状态.鼠标的位置.鼠标按钮的状态. 什 ...

  6. jquery $.trim()去除字符串空格详解

    jquery $.trim()去除字符串空格详解 语法 jQuery.trim()函数用于去除字符串两端的空白字符. 作用 该函数可以去除字符串开始和末尾两端的空白字符(直到遇到第一个非空白字符串为止 ...

  7. 010-Scala单例对象、伴生对象实战详解

    010-Scala单例对象.伴生对象实战详解 Scala单例对象详解 函数的最后一行是返回值 子项目 Scala伴生对象代码实战 object对象的私有成员可以直接被class伴生类访问,但是不可以被 ...

  8. openerp经典收藏 对象定义详解(转载)

    对象定义详解 原文地址:http://shine-it.net/index.php/topic,2159.0.htmlhttp://blog.sina.com.cn/s/blog_57ded94e01 ...

  9. “全栈2019”Java第一百一十三章:什么是回调?回调应用场景详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

随机推荐

  1. struts2+spring的两种整合方式

    也许有些人会因为学习了struts1,会以为struts2.struts1与spring的整合也是一样的,其实这两者相差甚远.下面就来讲解一下struts2与spring的整合两种方案.(部分转载,里 ...

  2. GJM :异步Socket [转载]

    原帖地址:http://blog.csdn.net/awinye/article/details/537264 原文作者:Awinye 目录(?)[-] 转载请原作者联系 Overview of So ...

  3. (原) 2.1 Zookeeper原生API使用

    本文为原创文章,转载请注明出处,谢谢 Zookeeper原生API使用 1.jar包引入,演示版本为3.4.6,非maven项目,可以下载jar包导入到项目中 <dependency> & ...

  4. css3图片模糊过滤效果

    css3图片过滤效果,鼠标放上后其它图片模糊,鼠标所在位置的图片是清淅的,有效索引出当前的图片,对图片的模糊处理是本特效的亮点,你完全可以将模糊的效果应用于其它的图片特效中,你同样也可借此代码研究一下 ...

  5. JavaScript数组的reduce方法详解

    数组经常用到的方法有push.join.indexOf.slice等等,但是有一个经常被我们忽略的方法:reduce,这个方法简直强大的不要不要的. 我们先来看看这个方法的官方概述:reduce()  ...

  6. HTML5自定义属性之data-*

    HTML5 增加了一项新功能是 自定义数据属性 ,也就是  data-* 自定义属性.在HTML5中我们可以使用以 data- 为前缀来设置我们需要的自定义属性,来进行一些数据的存放.当然高级浏览器下 ...

  7. iOS 多线程GCD简介

    一.简介 1.1 GCD (Grand Central Dispatch )是Apple开发的一个多核编程的解决方法. Grand 含义是“伟大的.宏大的”,Central含义“中央的”,Dispat ...

  8. 将oracle冷备份恢复到另外一个数据库实例中

    因更换服务器需要将Oracle数据库转移到另外台Oracle中.说明: 1.测试环境为:windows server2003 和 oracle 10g. 2.2台服务器安装的程序目录一样,数据目录不一 ...

  9. jQuery介绍 DOM对象和jQuery对象的转换与区别

    jQuery介绍 DOM对象和jQuery对象的转换与区别 jQuery介绍      jQuery: http://jquery.com/      write less, do more.   j ...

  10. GPU大百科全书索引(有助于理解openGL工作流程)

    GPU大百科全书索引 0.GPU大百科全书 前传 看图形与装修的关系 1.GPU大百科全书 第一章:美女 方程与几何 2.GPU大百科全书 第二章 凝固生命的光栅化 3.GPU大百科全书 第三章:像素 ...