$.Callbacks 用于管理函数队列,通过 add 添加处理函数到队列中,通过 fire 去执行这些处理函数。
本节向大家介绍 $.Callbacks 的实现的原理,并简单实现一个自己的 callbacks。
$.Callbacks 用于管理函数队列,通过 add 添加处理函数到队列中,通过 fire 去执行这些处理函数。
本节向大家介绍$.Callbacks 的实现的原理,并简单实现一个自己的 callbacks。
概念解读
从事件函数了解 Callbacks,事件通常与函数配合使用,这样就可以通过触发事件来驱动函数的执行。
原则上,一个事件对应一个事件函数。在一个事件对应多个事件函数的情况下,后者会覆盖前者。
ele.onclick = function(){ console.log("code")}ele.onclick = function(){ console.log("code1")}
上边这个 Demo 中后面绑定的这个事件函数会覆盖前边的,事件触发时会打印"code1"。
事件驱动改造
如果想让触发事件时执行多个函数,是否可行呢?
当然可以,我们可以把需要执行的多个函数放在一个数组里,事件触发时循环执行这个数组里的函数。下面看一下伪代码:
var callbacks = [function a(){}, function b(){}, function c(){}];ele.onclick = function(){ var _this = this; callbacks.forEach(function(fn){ fn.call(_this); });}
而 Callbacks 并不仅仅是一个数组,而是一个容器。
$.Callbacks API 的使用
基础应用
// 1. $.Callbacks()返回 Callbacks 的实例对象var cb = $.Callbacks();// 2. 方法 add()向内部队列添加函数cb.add(() => { console.log(1)});// 3. 方法 fire()传入参数执行队列里的函数cb.fire();// 控制台结果:1
Callbacks 参数
$.Callbacks()通过字符串参数的形式,支持 4 种特定的功能:once
,unique
,stopOnFalse
,memory
once 函数队列只执行一次
// 不添加参数var cb = $.Callbacks();cb.add(() => { console.log(1)});cb.fire(); // 控制台打印: 1cb.fire(); // 控制台打印: 1
如果不指定参数,调用 fire()方法两次,控制台将打印两次 1
// 添加参数var cb = $.Callbacks("once");cb.add(() => { console.log(1)});cb.fire(); // 控制台打印: 1cb.fire();
如果不指定参数为字符串once
,调用 fire()方法两次,控制台只打印一次 1,需要注意:这里的字符串是区分大小写
unique 使添加到内部函数队列里的函数保持唯一,不能重复添加
// 不加参数 var cb = $.Callbacks();var test = function () { console.log("unique test");};cb.add(test, test);cb.fire(); // 控制台打印: // unique test // unique test
我们通过 add()方法往函数队列里添加了两个test
函数,调用 fire()方法,控制台打印了两次“unique test”。
// 不加参数 var cb = $.Callbacks(“unique”);var test = function () { console.log("unique test");};cb.add(test, test);cb.fire(); // 控制台打印: unique test
指定参数unique
,即使我们往函数队列里添加了两个test
函数,调用 fire()方法,控制台也只会打印一次“unique test”。
stopOnFalse 内部函数队列顺序依次执行,当某个函数返回值为 false 时,停止该函数后边的函数继续执行
// 不添加参数var cb = $.Callbacks();var test1 = function () { console.log("stopOnFalse one");};var test2 = function () { console.log("stopOnFalse two"); return false;};var test3 = function () { console.log("stopOnFalse three");};cb.add(test1, test2, test3);cb.fire()// 控制台打印:// stopOnFalse one// stopOnFalse two// stopOnFalse three
不添加参数时函数队列依次执行
// 添加参数var cb = $.Callbacks("stopOnFalse");var test1 = function () { console.log("stopOnFalse one");};var test2 = function () { console.log("stopOnFalse two"); return false;};var test3 = function () { console.log("stopOnFalse three");};cb.add(test1, test2, test3);cb.fire()// 控制台打印:// stopOnFalse one// stopOnFalse two
指定参数为stopOnFalse
,当函数执行到 test2 时,因为该函数返回了 false,所以后边的 test3 将不再执行
- memory 当函数队列 fire 一次过后,内部会记录当前 fire 方法的参数。当下次调用 add 时,会把记录的参数传递给新添加的函数并立即执行这个新添加的函数。
// 不添加参数var cb = $.Callbacks();var test1 = function () { console.log("memory one");};var test2 = function () { console.log("memory two");};cb.add(test1);cb.fire(); // 控制台打印:memory onecb.add(test2);
// 添加参数var cb = $.Callbacks("memory");var test1 = function () { console.log("memory one");};var test2 = function () { console.log("memory two");};cb.add(test1);cb.fire(); // 控制台打印:memory one memory twocb.add(test2);
$.Callbacks 实现
参考 jQuery 的 Callbacks,我们自己来实现一下。
基本结构
(function (root) { var _ = { callbacks: function () { console.log("test"); } } root._ = _;})(this);_.callbacks(); // test
通过一个闭包把执行上下文传入,因为在浏览器里执行这里传入的是 Window 对象,把_
做为 root 的属性,这样我们在全局就可以访问_
。
(function (root) { var optionsCache = {}; var _ = { callbacks: function (options) { // 判断传入的参数类型 // 字符串:存储在 optionsCache 对象里,如果该对象已经存在这个属性直接使用,如果不存在则创建这个属性值为 true options = typeof options === "string" ? (optionsCache[options] || createOptions(options)) : {}; console.log(options); // {"once":true,"memory":true} console.log(optionsCache) // {"once":true,"memory":true} } } root._ = _; var createOptions = function (options) { var object = {}; // 如果同时指定两种类型,传入的参数为“once memory”,需拆解成单独的字符串 options.split(/\s+/).forEach(value => { object[value] = optionsCache[value] = true; }); return object; }})(this);_.callbacks("once memory");
上边这个函数支持获取用户传过来的参数,并且支持用户同时指定多个类型的参数,并把这些参数存储在optionsCache
缓存对象中。接下来看一下add()
,fire()
方法的实现。
(function (root) { var optionsCache = {}; var _ = { callbacks: function (options) { // 判断传入的参数类型 // 字符串:存储在 optionsCache 对象里,如果该对象已经存在这个属性直接使用,如果不存在则创建这个属性值为 true options = typeof options === "string" ? (optionsCache[options] || createOptions(options)) : {}; var self = { add: function () { console.log("add"); }, fire: function () { console.log("fire"); } } return self; } } root._ = _; var createOptions = function (options) { var object = {}; // 如果同时指定两种类型,传入的参数为“once memory”,需拆解成单独的字符串 options.split(/\s+/).forEach(value => { object[value] = optionsCache[value] = true; }); return object; }})(this);
var cb = _.callbacks();cb.add(); // addcb.fire(); // fire
在 callbacks 函数中创建 self 对象,并添加 add 方法和 fire 方法,然后把这个 self 返回,这样调用 callbacks 函数后就可以获取这个 self。参考上面的代码。下一步,我们来实现一下 add 方法和 fire 方法。
add 方法和 fire 方法的实现
(function (root) { var optionsCache = {}; var _ = { callbacks: function (options) { // 判断传入的参数类型是否为字符串 // 存储在 optionsCache 对象里,如果该对象已经存在这个属性直接使用,如果不存在则创建这个属性值为 true options = typeof options === "string" ? (optionsCache[options] || createOptions(options)) : {}; var fnList = []; var index, length; var fire = function (data) { index = 0; length = fnList.length; for (; index < length; index++) { fnList[index].apply(data[0], data[1]); } } var self = { add: function () { // 将传入的参数转成数组 var argArr = Array.prototype.slice.call(arguments); argArr.forEach((fn) => { // 判断传入的参数是否为 function if (toString.call(fn) === "[object Function]") { fnList.push(fn); } }) }, fireWith: function (context, arguments) { var args = [context, arguments]; fire(args); }, fire: function () { self.fireWith(this, arguments); } } return self; } } root._ = _; var createOptions = function (options) { var object = {}; // 如果同时指定两种类型,传入的参数为“once memory”,需拆解成单独的字符串 options.split(/\s+/).forEach(value => { object[value] = optionsCache[value] = true; }); return object; }})(this);
var cb = _.callbacks();cb.add(function a(params) { console.log("a");}, function b(params) { console.log("b");}); cb.fire(); // a b
add()可以往函数队列里添加函数,fire()可以依次执行队列的函数。上面的代码 fire()是顺序执行队列,接下来实现通过指定 callbacks 的参数来控制函数队列的执行。
参数 stopOnFalse 功能
(function (root) { var optionsCache = {}; var _ = { callbacks: function (options) { // 判断传入的参数类型是否为字符串 // 存储在 optionsCache 对象里,如果该对象已经存在这个属性直接使用,如果不存在则创建这个属性值为 true options = typeof options === "string" ? (optionsCache[options] || createOptions(options)) : {}; var fnList = []; var index, length; var fire = function (data) { index = 0; length = fnList.length; for (; index < length; index++) { // 判断函数执行结果是否为 false 并且设置了 stopOnFalse // data[0]:执行上下文 // data[1]:执行时传入的参数 if (!(fnList[index].apply(data[0], data[1])) && options['stopOnFalse']) { break; } } } var self = { add: function () { // 将传入的参数转成数组 var argArr = Array.prototype.slice.call(arguments); argArr.forEach((fn) => { // 判断传入的参数是否为 function if (toString.call(fn) === "[object Function]") { fnList.push(fn); } }) }, fireWith: function (context, arguments) { var args = [context, arguments]; fire(args); }, fire: function () { self.fireWith(this, arguments); } } return self; } } root._ = _; var createOptions = function (options) { var object = {}; // 如果同时指定两种类型,传入的参数为“once memory”,需拆解成单独的字符串 options.split(/\s+/).forEach(value => { object[value] = optionsCache[value] = true; }); return object; }})(this);
// 不指定参数var cb = _.callbacks();cb.add(function a(params) { console.log("a"); return false;}, function b(params) { console.log("b");}); cb.fire(); // a b
// 指定参数为 stopOnFalsevar cb = _.callbacks("stopOnFalse");cb.add(function a(params) { console.log("a"); return false;}, function b(params) { console.log("b");}); cb.fire(); // a
上面代码实现了 calLbacks 指定参数为 stopOnFalse 时的效果。
参数 once 功能
(function (root) { var optionsCache = {}; var _ = { callbacks: function (options) { // 判断传入的参数类型是否为字符串 // 存储在 optionsCache 对象里,如果该对象已经存在这个属性直接使用,如果不存在则创建这个属性值为 true options = typeof options === "string" ? (optionsCache[options] || createOptions(options)) : {}; var fnList = []; var index, length, isFire; var fire = function (data) { index = 0; length = fnList.length; for (; index < length; index++) { // 判断函数执行结果是否为 false 并且设置了 stopOnFalse // data[0]:执行上下文 // data[1]:执行时传入的参数 if (!(fnList[index].apply(data[0], data[1])) && options['stopOnFalse']) { break; } } isFire = true; } var self = { add: function () { // 将传入的参数转成数组 var argArr = Array.prototype.slice.call(arguments); argArr.forEach((fn) => { // 判断传入的参数是否为 function if (toString.call(fn) === "[object Function]") { fnList.push(fn); } }) }, fireWith: function (context, arguments) { var args = [context, arguments]; if (!(options["once"] && isFire)) { fire(args); } }, fire: function () { self.fireWith(this, arguments); } } return self; } } root._ = _; var createOptions = function (options) { var object = {}; // 如果同时指定两种类型,传入的参数为“once memory”,需拆解成单独的字符串 options.split(/\s+/).forEach(value => { object[value] = optionsCache[value] = true; }); return object; }})(this);
// 不添加参数var cb = _.callbacks();cb.add(function a(params) { console.log("a"); return false;}, function b(params) { console.log("b");});cb.fire(); // abcb.fire(); // ab
// 添加参数 oncevar cb = _.callbacks("once");cb.add(function a(params) { console.log("a"); return false;}, function b(params) { console.log("b");});cb.fire(); // abcb.fire();
上面的代码实现了当设置参数为once
时,fire 调用多次,只会执行 1 次。
参数 memory 功能
(function (root) { var optionsCache = {}; var _ = { callbacks: function (options) { // 判断传入的参数类型是否为字符串 // 存储在 optionsCache 对象里,如果该对象已经存在这个属性直接使用,如果不存在则创建这个属性值为 true options = typeof options === "string" ? (optionsCache[options] || createOptions(options)) : {}; var fnList = []; var index, length, isFire, memory, start; var fire = function (data) { // 如果设置"memory",则记录当前传入的参数 memory = options["memory"] && data; index = start || 0; start = 0 length = fnList.length; for (; index < length; index++) { // 判断函数执行结果是否为 false 并且设置了 stopOnFalse // data[0]:执行上下文 // data[1]:执行时传入的参数 if (fnList[index].apply(data[0], data[1]) === false && options['stopOnFalse']) { break; } } isFire = true; } var self = { add: function () { // 将传入的参数转成数组 var argArr = Array.prototype.slice.call(arguments); argArr.forEach((fn) => { // 判断传入的参数是否为 function if (toString.call(fn) === "[object Function]") { fnList.push(fn); } }) // 如果设置了 memory 参数,并且参数存在,则调用 if (memory) { // start 为次一次执行时的顺序 start = fnList.length - 1; fire(memory) } }, fireWith: function (context, arguments) { var args = [context, arguments]; if (!(options["once"] && isFire)) { fire(args); } }, fire: function () { self.fireWith(this, arguments); } } return self; } } root._ = _; var createOptions = function (options) { var object = {}; // 如果同时指定两种类型,传入的参数为“once memory”,需拆解成单独的字符串 options.split(/\s+/).forEach(value => { object[value] = optionsCache[value] = true; }); return object; }})(this);
var cb = _.callbacks("memory");cb.add(function a(params) { console.log("a"); return false;});cb.add(function c(params) { console.log("c");}, function d(params) { console.log("d");})cb.fire(); // a c d bcb.add(function b(params) { console.log("b");})
上面代码实现了memory
的功能。
阅读全文: http://gitbook.cn/gitchat/activity/5d79a3c7b7eb6c38f40e7952
您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。