还记得jQuery源码阅读(一)的时候,整理了jQuery库的整体架构,主要分为三个模块:
入口模块、底层支持模块和功能模块,各个模块之间也是有关联的。
前面几篇博文分别分析了jQuery库的入口模块,最主要的是init方法的分析;还分析了jQuery底层支持模块中的工具方法,主要是通过jQuery.extend()方法来扩展jQuery静态方法的。底层支持模块中,出了工具方法之外,还有很多模块,个人觉得这些模块的一个作用就是增强代码的复用性和易用性。比如回调对象模块,异步队列模块,延迟对象,队列,数据缓存,浏览器支持模块,Sizzle选择器模块等等。这次主要先整理jQuery中的回调对象。
源码框架及使用
jQuery.Callbacks = function(flags){
add = function(){
}
fire = function(){
}
self = {
//定义了一些变量
//下面是用来管理函数的方法
add: function(){
},
fire: function(){
},
fireWith: function(){
},
has: function(){
},
fired: function(){
},
lock: function(){
},
locked: function(){
}
//等等
}
return self;
}
由上面架构可以知道,jQuery.Callbacks函数主要是返回一个回调对象,这个回调对象通过一些方法来管理回调函数。具体什么方法呢?有add(),fire(),fireWith(),lock()等等很多方法,主要是通过将函数加入到数组列表中的方式,然后一个个去执行从而达到管理函数的目的。
可以看到,Callbacks方法里面,定义了两个私有函数,为什么要这么做呢?
我的理解是:为了减少内存的占用。因为Callbacks函数最终是要返回一个回调对象的,而回调对象中有管理函数的一些方法,如果把这些方法直接写到回调对象中,也是可以的,但是这就意味着每个回调对象都会有一份自己的方法,只是这些方法都是同名的,这就导致内存的浪费。而把add和fire定义成两个私有的变量,不管调用多少次jQuery.Callbacks方法,返回的回调对象中的方法都会去调Callbacks函数中的私有方法,并且指向的是相同的内存地址。
下来我们看下jQuery.Callbacks的参数情况:
1. once //只执行一遍函数,不会重复执行
2. memory //会将所有add进函数列表的函数执行
3. unique //不允许列表中有相同的函数
4. stopOnFalse //对于返回值为false的函数,执行之后停止对后面函数的执行
首先来体会下这几个参数的作用:
function a1(){
console.log('111');
return false;
}
function a2(){
console.log('222');
}
分别展示四组图来显示Callbacks中四个参数的意义:
- once标志
- memory标志
- unique标志
- stopOnFalse标志
从上面四组图可以很容易理解四个参数的含义,那么这四个参数具体在源码中是如何影响的?我想用下面的图来说明
源码分析
根据上面那个图,我们就大致应该知道:
add函数中,将函数fn Push到list数组里,同时会判断unique和memory两个参数,如果unique参数为真,那么不会将相同的函数都push到数组里;如果memory为真,会add之后再调一次fire函数。
fire函数中,将数组中所有的函数进行执行,同时会判断once和stopOnFalse两个参数。如果once为真,只会将所有函数执行一遍;如果stopOnFalse为真,那么对于返回值为false的函数,执行之后不会再对后面的函数执行。
基于上面两块思路来看源码,会更好理解一些。
add方法
回调对象中的add方法:
add: function() {
if ( list ) {
var length = list.length;
//这里的add方法是Callbacks函数中的私有方法
add( arguments );
if ( firing ) {
firingLength = list.length;
} else if ( memory && memory !== true ) {
//判断memory参数,并且调fire函数
firingStart = length;
fire( memory[ 0 ], memory[ 1 ] );
}
}
return this;
}
Callbacks函数中的私有add方法:
add = function( args ) {
var i,
length,
elem,
type,
actual;
for ( i = 0, length = args.length; i < length; i++ ) {
elem = args[ i ];
type = jQuery.type( elem );
if ( type === "array" ) {
//如果参数为数组,再递归调add方法
add( elem );
} else if ( type === "function" ) {
//判断unique标志,并且看list数组中是否有该函数
if ( !flags.unique || !self.has( elem ) ) {
list.push( elem );
}
}
}
}
fire方法
self对象中的fire函数:
fire: function() {
//fire方法去调self对象的fireWith方法,该方法不仅将当前作用域传了进去,还将fire中的参数也传进去了
self.fireWith( this, arguments );
return this;
}
fireWith: function( context, args ) {
if ( stack ) { //一开始stack为空数组,转换成Boolean值也是真;设置了once,会将stack置为undefined,所以重复的fire都不会真正执行
if ( firing ) { //firing这个标志是解决嵌套调用fire的情况;与stack结合起来处理,stack用于保存上下文和参数
if ( !flags.once ) {
stack.push( [ context, args ] );
}
} else if ( !( flags.once && memory ) ) { //第一次执行或者未设置once参数多次执行
fire( context, args );
}
}
return this;
}
fire = function( context, args ) {
args = args || [];
//如果未设置memory参数,那么memory变量为true;
memory = !flags.memory || [ context, args ];
fired = true;
firing = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
//对list中每一个函数执行,如果返回false并且stopOnFalse参数为真,那么跳出循环,不再对后面的函数执行。
if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
memory = true; // Mark as halted
break;
}
}
firing = false;
if ( list ) {
if ( !flags.once ) {
if ( stack && stack.length ) {
memory = stack.shift();
self.fireWith( memory[ 0 ], memory[ 1 ] );
}
} else if ( memory === true ) {
self.disable(); //这个用于将stack标记设为undefined
} else {
list = [];
}
}
}
对于Callbacks回调模块,主要就是这两个函数,当然,还有像self.has
, self.empty
, self.lock
, self.disable
等方法,不过相比来说,这几个方法都比较好理解。这里大概提下lock和disable方法,lock方法是禁止后面的fire函数; 而disable方法会禁止后面要执行的所有操作。总体上来说,Callbacks模块,如果能考虑到所有的情况,并可以沿着源码走一遍,那么对于整个模块已经理解得差不多了。