JQuery中的回调对象

回调对象(Callbacks object)模块是JQuery中的一个很基础的模块,很多其他的模块(比如Deferred、Ajax等)都依赖于Callbacks模块的实现。
本文主要描述回调对象的基本功能、具体实现,以及使用到的设计思想。

1. 基本功能

从jquery的API 中可以了解到,Callbacks对象是JQuery自1.7以后提供的一个管理回调函数的工具,提供基本的增加、删除、调用和禁用回调函数的能力。

1.1 基本配置

Callbacks提供如下四个配置供开发者使用:

  • once 保证所有回调函数只能执行一次。
  • memory 记录上一次调用回调函数的参数值。如果在调用回调函数之后又通过add新回调函数,那么会立即使用memory记录的值调用新增的回调函数。
  • unique 保证每个回调函数只能被添加一次,即回调函数列表中每个函数都唯一。
  • stopOnFalse 如果一个回调函数返回false,则终止调用其他剩余会回调函数。

开发者可以通过以上配置的字符串组合(以空格分隔)初始化回调对象。

var callbacks = $.Callbacks('unique memory');  //回调函数列表中每个函数必须唯一,且要记录上一次调用的参数值

1.2 基本API

Callbacks提供如下API:

  1. callbacks.add()
    向回调函数列表中增加一个回调函数或回调函数集合。
  2. callbacks.remove()
    从回调函数列表中删除一个回调函数或回调函数的集合。
  3. callbacks.disable()
    禁用改回调函数列表,不在执行任何动作。
  4. callbacks.disabled()
    回调函数列表是否被禁用。
  5. callbacks.empty()
    清空回调函数列表。
  6. callbacks.has()
    如果带有参数,则表示该参数是否在回调函数列表中,否则表示回调函数列表是否为空。
  7. callback.fire()
    指定参数调用列表中所有的回调函数。
  8. callback.fireWith()
    指定上下文和参数调用列表中所有的回调函数。
  9. callback.fired()
    回调函数是否被执行过。
  10. callback.lock()
    锁定回调函数列表,使其保持当前的状态不变。
  11. callback.locked()
    回调函数列表是否被锁定。

2. 具体实现

2.1 add和fire/fireWith

从上面的API中已经可以基本看出Callbacks的基本实现方式了,即维护一个回调函数列表(list)和一个参数队列(queue)。每次通过add向list中增加回调函数,然后通过fire/fireWith从queue中取出参数按序执行回调函数。
JQuery的Callbacks模块代码很简单,其核心就是一个内部的fire函数(非api中fire函数,请看下文fire介绍):

    // Actual callback list
list = [],
// Stack of fire calls for repeatable lists
stack = !options.once && [],
// Fire callbacks
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();
}
}
}

以上代码摘自JQuery-1.12版本,代码中的list就是回调函数列表,stack就是参数队列queue。可以看出,每次执行fire函数都会遍历list,用传进来的参数调用每个回调函数,如果参数队列不为空,则递归的执行,一直到参数队列取空为止。

我们再看add函数的代码:

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

add函数会根据参数类型将参数添加到回调函数列表中,即如果参数是一个函数(且要求unique),那么就直接放进回调函数列表中,如果参数是一个类数组,那么就递归调用add函数,将参数中的所有回调函数放进对调函数列表中。
请注意,如果在初始化Callbacks对象时设置了memory,那么在调用add函数的时候,会使用memory记住的参数值调用每一个新加的回调函数。

再看fire和fireWith函数:

    // 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 ];
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;
},

这两个函数将参数放进参数队列中,最终都调用内部fire函数将参数传递给回调函数。

2.2 disable和lock

此处要特别说明一下disable和lock的区别,先看这两个函数的实现:

    // Have the list do nothing anymore
disable: function() {
list = stack = memory = undefined;
return this;
}, // Lock the list in its current state
lock: function() {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},

这两个函数都禁用了回调函数队列,即不能再通过fire/fireWith调用回调函数。但是,如果我们在初始化Callbacks对象的时候,设置了memory的话,那么当回调函数被lock之后,通过add新增的回调函数依然可以使用memory的值调用。

3. 总结

不难看出,整个Callbacks的设计思想就是基于发布订阅(Pub/Sub)的观察者模式。与一般实现方式相比较而言,一个对象不再直接调用另一个对象的方法,而是观察另一个对象的某个特定的任务或活动,这个观察的对象就叫订阅者(Subscriber),被观察的对象就叫发布者(Publisher),当发布者的任务或活动完成时,会通知订阅者。
这种设计模式的主要目的就是为了达到程序之间的松耦合。

最新文章

  1. 练习2 练习目标-使用引用类型的成员变量:在本练习中,将扩展银行项目,添加一个(客户类)Customer类。Customer类将包含一个Account对象。
  2. mysql访问连接过多
  3. FFT质数打表程序
  4. javascript中闭包的概念
  5. 【分享】Matlab R2015a 发布啦!
  6. codeforces723 D. Lakes in Berland(并查集)
  7. bzoj 1041 圆上的整点 分类: Brush Mode 2014-11-11 20:15 80人阅读 评论(0) 收藏
  8. 【转】can&#39;t find referenced method &#39;android.app.RemoteInput[] getRemoteInputs()&#39; in class android.app.Notification$Action
  9. 转:Google论文之一----Bigtable学习翻译
  10. &lt;2&gt;集腋成裘
  11. IOS内存管理详解
  12. 13:python-ldap
  13. jqgrid 点击列头的超链接或按钮时,不触发列排序事件
  14. 集合list里存储list,要遍历最底层list的对象元素的遍历方法
  15. JS: 防抖节流
  16. Java基于ssm框架的restful应用开发
  17. D3.js学习笔记(六)——SVG基础图形和D3.js
  18. 常用数据结构的功能及复杂度总结(OI)
  19. [19/04/28-星期日] GOF23_结构型模式(享元模式)
  20. SharePoint Server 2013 Offline Installation (without Internet)

热门文章

  1. [图形学] 结束 [Unity Shader] 开始
  2. Java数据类型在实际开发中的应用一
  3. geotrellis使用(二十九)迁移geotrellis至1.1.1版
  4. 自定义程序异常Exception
  5. USB的前世今生
  6. 利用TinyXml进行数据库的热更新
  7. Echarts折线图表断点如何补全
  8. sqlserver的触发器练习实例
  9. business meeting
  10. 腾讯 AlloyCrop 1.0 发布