jQuery:-$.Callbacks 实现原理

$.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 将不再执行

  1. 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 专享技术内容哦。

jQuery:-$.Callbacks 实现原理

上一篇:汉字截断问题


下一篇:command failed shell "ps 'uiautomator'"的解决方式