4.jQuery.Callbacks大致意义讲解

jQuery.Callbacks是一个用于回调函数管理的工具,它提供了once、memory、unique、stopOnFalse等选项,支持fire、add、remove等操作。文章介绍了其核心功能和选项的作用,并通过代码示例展示了如何使用这些功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

jQuery.Callbacks:

作用是对加载的回调函数进行统一管理。有once、memory、unique、stopOnFalse四种标志,有fire、add、remove、has、empty、disable、disabled、lock、locked、fireWith、fired这些操作。

需要前置的了解一些知识:

  • core_rnotwhite = /\S+/g:正则,用来匹配任何非空白字符(空白字符,包括空格、制表符、换页符等等)。
  • var optionsCache = {}:一个空对象,用于存储缓存选项
  • createOptions
function createOptions(options) {
	var object = optionsCache[options] = {};
	jQuery.each(options.match(core_rnotwhite) || [],
	function(_, flag) {
		object[flag] = true;
	});
	return object;
}

传入一个字符串(由空格分隔选项),然后匹配,将字符串分割,然后遍历,作为object的属性,内容为true,返回对象。结果如下:

代码详解:

大体框架:

jQuery.Callbacks = function( options ) {
    options = ......

	var
		memory,
		fired,
		firing,
		firingStart,
		firingLength,
		firingIndex,
		list = [],
		stack = !options.once && [],
        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() {
                ......
            }
        }
	return self;
}

可以了解到,函数返回的是self这个对象,通过这个对象的属性方法对内部数据进行操作。按代码长度来看,比较关键的是fire、add。

以下是简单的功能演示:能够执行加载的函数。

下面的函数解析,不是根据代码顺序解析的。

options:

根据传入的参数,将他赋值给内部的options,传入的options的作用是决定回调函数的执行方式,类似于配置。

options = typeof options === "string" ?
	( optionsCache[ options ] || createOptions( options ) ) :
	jQuery.extend( {}, options );

如果是字符串的话,如果optionsCache[options]以及存在就直接返回optionsCache[options](说明是单个配置项),反之就调用createOptions对options进行解析,返回结果。如果是对象的话,就先赋值给一个空对象,在复制给options。

(看注释的话,就可以知道,options有once、memory、unique、stopOnFalse四类)

以下是简单的功能演示:各个标志的作用:

once:只执行一次fire();

memory:之后加载的也能执行

unique:不会重复触发同一个函数

stopOnFalse:return false之后停止执行 

memory、fired、firing、firingStart、firingLength、firingIndex、list、stack

  • memory:memory标识
  • fired:标识,判断是否list中的函数已经执行过了。
  • firing:标识,判断是否有list中的函数正在执行。
  • firingStart:第一个被执行的函数在list列表中下标。
  • firingLength:标识,正在执行时,list列表中函数的数量。
  • firingIndex:正在执行的函数,在list中的下标。
  • list:用来存储函数的列表。
  • stack:用于暂时存储执行时再次执行函数元素。
     

has:

has: function( fn ) {
    return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},

输入的参数,如果参数存在的话,使用jQuery.inArray,判断该函数是否存在于list之中,返回true或者false,如果参数不存在的话,返回list是否存在函数的判断结果。

add:

add: function() {
	if ( list ) {
		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" ) {
					add( arg );
				}
			});
		})( arguments );
		if ( firing ) {
			firingLength = list.length;
		} else if ( memory ) {
			firingStart = start;
			fire( memory );
		}
	}
	return this;
},

声明一个start变量为列表的长度,然后一个匿名函数自执行,将add的参数数组作为参数传入匿名函数,然后使用jQuery.each遍历执行。如果元素类型是函数的话,就直接push压入list中(如果opions对象中,unique 为true且以及list中已经存在的话,就不压入list中了);如果参数存在,类型不是string且具有长度的话,就递归继续add。

如果此时list函数正在执行的话,将正在执行的长度加一(新添加的函数也加入到执行的列表中)。

如果memory为true的话,将开始执行函数的list下标,改为start,(即list.length,新加入的着个函数)执行fire。

(其实从这里可以看出,输入memory参数的话,相当于是fire()之后的add()自己执行fire())如下:

最后的hello打印,是add()执行的,而不是fire();因为hello并没有随着fire()执行两次。

remove:

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

根据输入的参数,使用jQuery.each遍历参数数组,移除list中存在的函数元素。如果正在fire执行的话,删除的元素所在的下标小于等于firingIndex的话,firingIndex--,删除的元素所在的下标小于等于firingLength的话,firingLength--。防止数组的改变对正在执行函数的影响。

disable:

disable: function() {
	list = stack = memory = undefined;
	return this;
},

将list、stack、memory 全部设置为undefined,即之后只调用fire()或者add()的话,就不会执行了fire()了。

self.fireWith、self.fire、fire:

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();
		}
	}
},
self = {
	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;
	},
	fire: function() {
		self.fireWith( this, arguments );
			return this;
	},
}

fire函数,实质上是在调用self.fireWith,将this(self)、fire的参数数组传入self.fireWith;

self.fireWith,先判断,如果列表存在,且第一次执行(!fired),或者stack 存在(stack 执行list的函数中时,调用fire()才会被添加)。如果args参数存在就去参数本身,不存在就取空数组,然后加上this。如果正在执行,就把参数压入stack(stack唯一一个增加元素的地方),如果不是正在执行的话,执行fire(args)。

fire函数,分两种方式,一种是执行list内的函数,从未开始到开始,即这一段代码:代表着firing = true;正在运行list中的函数。运行结束之后,firing = false

另一种就是完毕之后,处理于memory、once标志相关、stack正在执行相关的问题等,在执行中再次的执行(将fire()嵌套在被执行的函数中)。如果stack存在的话就执行fire,同时将函数元素移除栈,表示已经执行过了。stack只有设置为once是,才会使false,([]也是通过if判断的)。如果是memory,就list设置为空,表示只执行一次,如果

stack的嵌套处理如下:(加allow是为了防止循环)

empty:

disable: function() {
    list = [];
	firingLength = 0;
	return this;
},

清空数组,执行长度设置为0。

lock:

lock: function() {
	stack = undefined;
	if ( !memory ) {
		self.disable();
	}
	return this;
}

将list的内容锁定,如果memory存在的话,则执行disable。

locked:

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

返回stack状态,判断是否被锁住(once或者执行了disable),

fired:

fired: function() {
	return !!fired;
}

是否是执行过list内部的数组。

disabled:

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

返回,list状态的取反。作为disabled的状态标识。

小结:

简单的来说,jQuery.Callbacks就是围绕着存储函数的数组list,现实最简单增加和减少元素,然后是根据四个标识实现不同的执行,慢慢扩充。

这里对jQuery.Callbacks并不是一行行完全解析下来的,只讲了大致功能,解释的有点粗糙,详细的解析在后面。

jQuery.Callbacks虽然代码不多,但其中对函数周期流程的处理思想非常棒,对思维的要求较高,很值得学习。

源码:2880--------3042行

    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 // 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) {
            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;
    };

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值