一.前言
首先声明,这是本人个人笔记。不喜勿碰。
Callbacks在jQuery框架中是基础建设的中重之重,很多高级模块都是基于它实现的,事件模块,ajax模块。
在jQuery中回调对象模块其实就是一个队列,进行出列,插入队列的操作。只不过加了很多技巧,先贴出源码,其中有些调试笔记,可以参考下。
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)
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
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();
}
}
},
// 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 ) {
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;
},
// Remove a callback from the list
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );
// Handle firing indexes
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
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 ];
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.Callbacks(arg) arg可以是once,memory,unique,这个最后返回的是一个装有API的对象,封装了其自身的属性和几个方法。只能通过开放的API进行调用
传入的传参数不一样,他们的回调函数也会有不用一样的功能。
1.once
function a(){
console.log('a');
}
function b(){
console.log('b');
}
var callbacks = $.Callbacks('once');
callbacks.add( a );
callbacks.fire();
callbacks.add( b );
callbacks.fire();
输出: a
只激发了第一次添加的a函数,后面新增的然后在激发也没有执行.
只要激发一次以后,就不能在激发这个回调函数队列,因为在第一次激发后 self.disable();,禁用了该队列,尽管在ADD函数进去,也不能激发。
因为disable让list = stack = memory = undefined;
2.memory
var callbacks = $.Callbacks('memory');
callbacks.add( a );
callbacks.fire();
callbacks.add( b );
输出: a
b
先新增a函数,在激发,然后在新增b函数,按道理讲第二次新增b函数的时候没有激发,不会执行b函数,但是这就是memory的作用,按我的理解就是记忆,新增就是执行。
只不过在第一次激发之前没有记忆功能,但是在激发一次过后就有记忆的能力,你下次在新增函数的时候就直接运行.
只要激发一次后,激活memory,然后每次add函数的时候,就会立即执行该函数. 随时都可以激发该回调队列
3.unique
var callbacks = $.Callbacks('unique');
callbacks.add( a );
callbacks.add( a );
callbacks.fire();
输出: a
为什么只执行了一次a函数,不是add了两次吗?这就是unique的作用,相同的函数在函数队列list里面只能被添加一次
if ( !options.unique || !self.has( arg ) ) { // 不唯一||不存在
list.push( arg );
}
在add函数到队列的时候,检查是否已经添加过,如果添加过,则无视,否则就添加家进去,随时都可以激发该回调队列
二.jQuery应用回调模块
jQuery中事件和AJAX模块都用了这个回调模块,但是大部分都是jQuery.Callbacks('once memory')来构造
once和memory合起来,能让这个回调队列有什么功能呢?
1.能多次新增函数,多次激发
2.不过每次激发完后,之前添加进去的函数在list里面都会被清空 list = [] 。 而once方式是让list = undefined.
3.就是新增进去的函数只能执行一次,执行完被清空。
三,总结
jQuery运用了很对技巧,只是一个简单的队列,却会如此神奇。