读jQuery之十九(多用途回调函数列表对象)
$.Callbacks 是在版本 1.7 中新加入的。它是一个多用途的回调函数列表对象,提供了一种强大的方法来管理回调函数队列。
整个 $.Callbacks 的源码不到 200 行,它是一个工厂函数,使用函数调用方式(非new,它不是一个类)创建对象,它有一个可选参数flags用来设置回调函数的行为。
.ajax,$.Deferred 等组件提供基础功能的函数。它也可以用在类似功能的一些组件中,如自己开发的插件。
$.Callbacks构造的对象(以callbacks示例)主要包括以下方法:
- callbacks.add
- callbacks.remove
- callbacks.has
- callbacks.empty
- callbacks.disable
- callbacks.fire
- callbacks.fireWith
- callbacks.fired
- callbacks.lock
- callbacks.locked
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function fn1() { console.log(1) } function fn2() { console.log(2) } var callbacks = $.Callbacks(); // 方式1 callbacks.add(fn1); // 方式2 一次添加多个回调函数 callbacks.add(fn1, fn2); // 方式3 传数组 callbacks.add([fn1, fn2]); // 方式4 函数和数组掺和 callbacks.add(fn1, [fn2]); |
传数组进去实际在add内部判断如果是数组会递归调用私有的add函数。此外,需注意add方法默认不去重,比如这里fn1添加两次,fire时会触发两次。私有add方法有趣,它使用了具名函数立即执行其名仅在函数内可用。如图中所圈之处
2. callbacks.remove 从回调队列中删除一个函数
1 2 3 4 5 6 7 8 9 | function fn1() { console.log(1) } function fn2() { console.log(2) } var callbacks = $.Callbacks(); callbacks.add(fn1, fn2); callbacks.remove(fn1); |
此时fire只会触发fn2了。
此外remove方法会把添加多次的函数如fn1,全部删除掉。为此还和cmc和nick讨论为啥remove内部不用if而用while(开始我以为是jQuery写错了)。如下
1 2 3 | var callbacks = $.Callbacks(); callbacks.add(fn1, fn2, fn1, fn2); callbacks.remove(fn1); |
此时会把add两次的fn1都删掉,fire时只触发fn2两次。换成if则只删fn1一次。
3. callbacks.has 判断是否添加过某回调函数,不想重复添加时很有用
1 2 3 4 5 6 7 8 | function fn1() { console.log(1) } var callbacks = $.Callbacks(); callbacks.add(fn1); if (!callbacks.has(fn1)) { callbacks.add(fn1); } |
1 2 3 4 5 6 7 8 9 10 | function fn1() { console.log(1) } function fn2() { console.log(2) } var callbacks = $.Callbacks(); callbacks.add(fn1); callbacks.add(fn2); callbacks.empty(); |
此时再fire不会触发任何函数。empty函数实现很简单,只是把内部的队列管理对象list重置为一个空数组。这里可以了解清空数组的几种方式。
1 2 3 4 5 6 7 8 9 10 11 12 | function fn1() { console.log(1) } function fn2() { console.log(2) } var callbacks = $.Callbacks(); callbacks.disable(); callbacks.add(fn1); // 不起作用 callbacks.add(fn2); // 不起作用 callback.remove(fn1); // 不起作用 callbacks.fire(); // 不起作用 |
调用后再使用add, remove, fire等方法均不起作用。该方法内不是实际是将队列管理对象list、stack、memory都置undefined了。
1 2 3 4 5 6 7 | function fn() { console.log( this ); // 上下文是callbacks console.log(arguments); // [3] } var callbacks = $.Callbacks(); callbacks.add(fn); callback.fire(3); |
前面已经提到了,fire方法用来触发回调函数,默认的上下文是callbacks对象,还可以传参给回调函数。
7. callbacks.fireWith 同fire,但可以指定执行上下文
1 2 3 4 5 6 7 8 | function fn() { console.log( this ); // 上下文是person console.log(arguments); // [3] } var person = {name: 'jack' }; var callbacks = $.Callbacks(); callbacks.add(fn); callback.fireWith(person, 3); |
其实fire内部调用的是fireWith,只是将上下文指定为this了,而this正是$.Callbacks构造的对象。
8. callbacks.fired 判断是否有主动触发过(调用fire或fireWith方法)
1 2 3 4 5 6 7 8 | function fn1() { console.log(1) } var callbacks = $.Callbacks(); callbacks.add(fn1); callbacks.fired(); // false callbacks.fire(); callbacks.fired(); // true |
注意,只要调用过一次fire或fireWith就会返回true。
$.Callbacks构造时可配置的参数Flags是可选的,字符串类型以空格分隔,有如下
1 2 3 4 5 6 7 | function fn() { console.log(1) } var callbacks = $.Callbacks( 'once' ); callbacks.add(fn); callbacks.fire(); // 打印1 callbacks.fire(); // fn不再触发 |
1 2 3 4 5 6 7 8 9 10 | function fn1() { console.log(1) } function fn2() { console.log(2) } var callbacks = $.Callbacks( 'memory' ); callbacks.add(fn1); callbacks.fire(); // 必须先fire callbacks.add(fn2); // 此时会立即触发fn2 |
memory有点绕,本意是记忆的意思。实际它的用法有点诡异,需结合特定场景来看(如jQuery.Deferred)。当首次调用fire后,之后每次add都会立即触发。比如先callbacks.fire(),再callbacks.add(fn1),这时fn1会立即被调用。
如果是批量添加的,也都会被触发
1 2 3 4 5 6 7 8 9 10 11 12 13 | function fn1() { console.log(1) } function fn2() { console.log(2) } function fn3() { console.log(3) } var callbacks = $.Callbacks( 'memory' ); callbacks.add(fn1); callbacks.fire(); callbacks.add([fn2, fn3]); // output 2, 3 |
1 2 3 4 5 6 7 8 9 10 | function fn1() { console.log(1) } function fn2() { console.log(2) } var callbacks = $.Callbacks( 'unique' ); callbacks.add(fn1); callbacks.add([fn1, fn2]); // 再次添加fn1 callbacks.fire(); // output 1, 2 |
这个很好理解,之前用has判断去重,使用unique属性则更方便。本例先add一次fn1,第二次再add时内部则会去重。因此最后fire时只输出“1,2”而不是“1,1,2”。
4. stopOnFalse 回调函数返回false时中断回调队列的迭代
1 2 3 4 5 6 7 8 9 10 11 12 13 | function fn1() { console.log(1) } function fn2() { console.log(2) return false // 注意这里 } function fn3() { console.log(3) } var callbacks = $.Callbacks( 'stopOnFalse' ); callbacks.add(fn1, fn2, fn3); callbacks.fire(); // output 1,2 |
从该属性名就能知了它的意图,即回调函数通过return false来停止后续的回调执行。该示例添加了3个回调,fn2中使用return false,当fire执行到fn2时会停止执行,后续的fn3就不会被调用了。
用$.Callbacks实现观察者模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // 观察者模式 var observer = { hash: {}, subscribe: function (id, callback) { if ( typeof id !== 'string' ) { return } if (! this .hash[id]) { this .hash[id] = $.Callbacks() this .hash[id].add(callback) } else { this .hash[id].add(callback) } }, publish: function (id) { if (! this .hash[id]) { return } this .hash[id].fire(id) } } // 订阅 observer.subscribe( 'mailArrived' , function () { alert( '来信了' ) }) observer.subscribe( 'mailArrived' , function () { alert( '又来信了' ) }) observer.subscribe( 'mailSend' , function () { alert( '发信成功' ) }) // 发布 setTimeout( function () { observer.publish( 'mailArrived' ) }, 5000) setTimeout( function () { observer.publish( 'mailSend' ) }, 10000) |
注:阅读版本为1.8.3
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 单线程的Redis速度为什么快?
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库