jQuery Callbacks:
简单理解为:一个容器,里面可以存放多个函数,有add方法往容器里面填加函数,有fire方法把容器里面的所有函数都依次执行一遍。
创建容器:$.Callback([string])
创建一个Callback实例,可选传参数:
- once : 当调用fire方法时,容器里的函数只执行1次
- unique:去重,执行add方法时,检测有没有添加过
- stopOnfalse :当调用fire方法时,容器里的函数返回值为false时,停止执行后面的函数
- memory:当执行过一次fire后,再执行add时,会立即执行添加的新函数
Note:也可以理解为一个队列,不过队列的概念比较大,Callbacks并不具有队列的所有特性
使用方法:参数可以组合使用,例如:”once memory”
let cb = $.Callbacks();$.Callbacks('once')$.Callbacks('unique')$.Callbacks('stopOnfalse')$.Callbacks('memory')$.Callbacks('memory once')
实现:
- 第一个版本:尝试写一个简单的Callbacks。
function Callbacks(){let list = [];//返回一个有add和fire方法的对象,因为每次调用callbacks都需要是一个全新的对象return {add:function(fn){if(toString.call(fn)==='[object Function]'){list.push(fn);}},fire:function(){list.forEach(fn=>fn());}};}let cbs = Callbacks();cbs.add(function(){console.log('Hello!');})cbs.add(function(){console.log('How are you?');})cbs.fire() //Hello! How are you?cbs.fire() //Hello! How are you? (再次调用fire,则再次执行容器中所有的方法)
在上面的例子中,并没有考虑到Callbacks的可选参数,也就是要告诉容器要遵循的运行规则,共有四种规则:once、unique、stopOnfalse、memory,而且这些规则可以组合使用。
- 第二个版本: 考虑4种规则。
// rules: once, unique, memory, stopOnFalsefunction Callbacks(rules) {var list = [];var rObj = createRules(rules);var isFired = false;return {add: function(fn) {if(toString.call(fn) === '[object Function]') {// 需要检查uniqueif(rObj.unique) {list.indexOf(fn) === -1 && list.push(fn);}else {list.push(fn);}// 如果有memory,且fire执行过,则立即执行当前fnif(rObj.memory && isFired) {fn();}}return this;},fire: function() {if(!rObj.once || !isFired) {for(var i = 0; i< list.length; i++) {if(list[i]() === false && rObj.stopOnFalse) {break;}}isFired = true;}}};//通过callback('once memory')创建容器时,此时obj:{once:true,memory:true}function createRules(rules) {var rs = {};if(typeof rules === 'string') {rules.split(/\s+/).forEach((r) => {rs[r] = true;});}return rs;}}var cb1 = new Callbacks('unique');var f1 = function() {console.log('hello');return false;};var f2 = function() {console.log('How are you!');return false;};cb1.add(f1);cb1.add(f2);cb1.add(f1);cb1.fire();
在上面的例子中,Callbacks功能已经很完善了,支持了4中规则,还可以组合搭配使用。
- Callbacks在 jQuery中的实现
var cb = _.callbacks('memory');cb.add(function(){console.log('AAA');});cb.add(function(){console.log('BBB');return false;});cb.fire();cb.add(function(){console.log('CCC');});cb.add(function(){console.log('DDD');});
(function(root) {var optionsCache = {};var _ = {callbacks: function(options){options = typeof options === 'string' ? (optionsCache[options] || createOptions(options)) : {};var list = [], testing, length, index, start, starts, memory;var fire = function(data) {memory = options.memory && data;testing = true;length = list.length;index = starts || 0; // 执行下标start = 0;for(; index < length; index++) {if(list[index].apply(data[0], data[1]) === false && options.stopOnFalse) {break;}}}var self = {add: function() {// add可以传入一个function,也可能多个function; 将auguments转成真正的数组var args = Array.prototype.slice.call(arguments);start = list.length;args.forEach((fn) => {if(toString.call(fn) === '[object Function]') {list.push(fn);}});// 是否为memory,如果是,执行下标要从队列的最后一个函数执行// 如果是'memory',第一次add的时候,memory为空,因为没有fire过,所以不会执行这里if(memory) {starts = start;fire(memory);}},fireWith: function(context, arguments) {var agrs = [context, arguments];if(!options.once || !testing) {fire(agrs);}},fire: function() {self.fireWith(this, arguments);}};return self;}}//通过callback('once memory')创建容器时,此时obj:{once:true,memory:true}function createOptions(options) {var object = optionsCache[options] = {};if(typeof options === 'string') {options.split(/\s+/).forEach((r) => {object[r] = true;});}return object;}root._ = _;})(this);
jQuery Callbacks 设计图:
可以把Callbacks理解为一个容器,或者一个队列(但队列的概念相对比较复杂),理解为容器更为简单和贴切。
Callbacks:并不是给用户专门使用的,而是对Deferred对象提供一种支持。

和ES6的Promise很像,设计灵感来自Deferred对象。
