一步一步DIY zepto库,研究zepto源码5-- callbacks

21 篇文章 0 订阅
8 篇文章 0 订阅

Callbacks API用来管理回调函数,也作为deferred延迟对象的基础部分,今天就一起来探寻它的源码(对应src下的callbacks.js)。

代码挂在我的github上,对应文件夹v0.5.1。
https://github.com/zrysmt/DIY-zepto

注:要在github源代码中自己编译的话,要在基础包命令:npm run dist上要进行扩展了,输入命令:

MODULES="zepto event ajax  callbacks" npm run dist
# on Windows
> SET MODULES=zepto event ajax  callbacks
> npm run dist

1.示例Demo

 var foo = function(value) {
     console.log('foo:' + value);
 };
 var bar = function(value) {
     console.log('bar:' + value);
 };

示例1:

var callbacks = $.Callbacks();
callbacks.add(foo);
callbacks.fire(['hello', 'wo', '123']);
callbacks.add(bar);
callbacks.fire('中');
 /*foo:hello,wo,123
   foo:中
   bar:中*/

标记:
* once: 回调只能触发一次
* memory 记录上一次触发回调函数列表时的参数,之后添加的函数都用这参数立即执行
* unique 一个回调函数只能被添加一次
* stopOnFalse 当某个回调函数返回false时中断执行

示例2:

var callbacks = $.Callbacks({
    memory: true
});
callbacks.add(foo);
callbacks.fire(['hello', 'wo', '123']);
callbacks.add(bar);
/*foo:hello,wo,123
  bar:hello,wo,123*/

示例3:

var callbacks = $.Callbacks({
    memory: true,
    once: true //只能执行一次
});
callbacks.add(foo);
callbacks.fire(['hello', 'wo', '123']);
callbacks.add(bar);
callbacks.fire(['hello', 'wo', '123']);
/*foo:hello,wo,123
  bar:hello,wo,123*/

2.整体结构

var Callbacks = function($) {
    $.Callbacks = function(options) {
        Callbacks = {/*add remove has empty等方法*/};
        return Callbacks;
    };
};
export default Callbacks;

3.源码

当然你也可以结合下一部分的过程分析,来理解源码。

3.1 几个重要变量

options = $.extend({}, options);

var memory, // Last fire value (for non-forgettable lists)
    fired, // Flag to know if list was already fired    //是否回调过
    firing, // Flag to know if list is currently firing  //回调函数列表是否正在执行中
    firingStart, // First callback to fire (used internally by add and fireWith) //第一回调函数的下标
    firingLength, // End of the loop when firing   //回调函数列表长度?
    firingIndex, // Index of currently firing callback (modified by remove if needed)
    list = [], // Actual callback list     //回调数据源: 回调列表
    stack = !options.once && [], // Stack of fire calls for repeatable lists//回调只能触发一次的时候,stack永远为false

3.2 fire函数– 回调底层函数

/**
 * 触发   回调底层函数
 */
fire = function(data) {
    memory = options.memory && data;
    fired = true;
    firingIndex = firingStart || 0;
    firingStart = 0;
    firingLength = list.length;
    firing = true; //正在回调
    //遍历回调列表,全部回调函数都执行,参数是传递过来的data
    for (; list && firingIndex < firingLength; ++firingIndex) {
        //如果 list[ firingIndex ] 为false,且stopOnFalse(中断)模式
        //list[firingIndex].apply(data[0], data[1])  这是执行回调
        //data经过封装,[context,arg] 第一个参数为上下文
        if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse) {
            memory = false; //中断回掉执行
            break;
        }
    }
    firing = false; //回调执行完毕
    if (list) {
        //stack里还缓存有未执行的回调
        if (stack) { //options.once存在的时候,不执行下面的一行
            stack.length && fire(stack.shift()); //执行stack里的回调
        } else if (memory) {
            list.length = 0; //memory 清空回调列表
        } else {
            Callbacks.disable(); //其他情况如  once 禁用回调
        }
    }
}

3.3 Callbacks对象

回调函数管理:添加add() 移除remove()、触发fire()、锁定lock()、禁用disable()回调函数。它为Deferred异步队列提供支持。

原理:通过一个数组保存回调函数,其他方法围绕此数组进行检测和操作

Callbacks = {
    add: function() {
        if (list) {
            var start = list.length,
                add = function(args) {
                    $.each(args, function(_, arg) {//$.each处理数组
                        if (typeof arg === "function") {
                            //非unique,或者是unique,但回调列表未添加过
                            if (!options.unique || !Callbacks.has(arg)) list.push(arg);
                        } else if (arg && arg.length && typeof arg !== 'string') {
                            //是数组/伪数组,添加,重新遍历   
                            add(arg);
                        }
                    });
                };
            add(arguments); //添加进列表
            if (firing) { //如果列表正在执行中,修正长度,使得新添加的回调也可以执行
                firingLength = list.length;
            } else if (memory) {
                firingStart = start; //memory 模式下,修正开始下标
                fire(memory); //立即执行所有回调
            }
        }
        return this;
    },
    //从回调列表里删除一个或一组回调函数
    remove: function() {
        if (list) { //回调列表存在才可以删除
            $.each(arguments, function(_, arg) {
                var index;
                while ((index = $.inArray(arg, list, index)) > -1) {
                    list.splice(index, 1); //执行删除
                    // Handle firing indexes
                    if (firing) {
                        //避免回调列表溢出
                        if (index <= firingLength) --firingLength; //在正执行的回调函数后,递减结尾下标
                        if (index <= firingIndex) --firingIndex; //在正执行的回调函数前,递减开始下标
                    }
                }
            });
        }
        return this;
    },
    //检查指定的回调函数是否在回调列表中

    has: function(fn) {
        return !!(list && (fn ? $.inArray(fn, list) > -1 : list.length));
    },
    //清空回调函数
    empty: function() {
        firingLength = list.length = 0;
        return this;
    },
    //禁用回掉函数
    disable: function() {
        list = stack = memory = undefined;
        return this;
    },
    //是否禁用了回调函数
    disabled: function() {
        return !list;
    },
    //锁定回调函数
    lock: function() {
        stack = undefined;
        //非memory模式下,禁用列表
        if (!memory) Callbacks.disable();
        return this;
    },
    //是否是锁定的
    locked: function() {
        return !stack;
    },
    //用上下文、参数执行列表中的所有回调函数
    fireWith: function(context, args) {
        // 未回调过,非锁定、禁用时
        if (list && (!fired || stack)) {
            args = args || [];
            args = [context, args.slice ? args.slice() : args];
            if (firing) {
                stack.push(args); //正在回调中,存入stack
            } else {
                fire(args); //否则立即回调,外层fire函数
            }
        }
        return this;
    },
    fire: function() {
        //执行回调
        return Callbacks.fireWith(this, arguments);
    },
    //回调列表是否被回调过
    fired: function() {
        return !!fired;
    }
};

4.过程分析

对于第一部分的示例:

var callbacks = $.Callbacks();
callbacks.add(foo);
callbacks.fire(['hello', 'wo', '123']);
  • add方法(CallBacks.add方法)
    • add函数(add(arguments))
      $.each可以处理参数是数组的形式
    • 非数组 直接存放到list中list.push(arg);
    • 数组 递归 add(arg)
    • 如果memory存在,为ture,离开执行 (fire(memory))
    • 返回this,可以链式调用
  • fire方法(CallBacks.fire方法)
    • Callbacks.fireWith(this, arguments)
    • 未回调过,非锁定、禁用时
      • 处理参数,包装成数组args = [context, args.slice ? args.slice() : args]</li>
        <li>如果正在执行回调(
        firing = truestack.push(args);//正在回调中,存入stack`
      • 否则 调用外层fire函数,立即执行。
  • 外层fire函数
    • 参数处理
      javascript
      memory = options.memory && data;
      fired = true;
      firingIndex = firingStart || 0;
      firingStart = 0;
      firingLength = list.length;
      firing = true; //正在回调
    • 遍历回调函数执行 for循环
    • 如果 list[ firingIndex ] 为false,且stopOnFalse(中断)模式
      立即中断memory = false; break;
    • 否则,立刻执行
      list[firingIndex].apply(data[0], data[1])
    • 处理结束 firing = false; //回调执行完毕
    • option.once为false,即stack为true
      stack.length && fire(stack.shift()); //执行stack里的回调
    • option.memory存在 则清空list【此时已经执行完结束了】
    • 其余情况,包括option.once为true,在遍历回调函数中已经执行过了,这里禁用回掉即可
      Callbacks.disable();

全部代码挂在我的github上,本博文对应文件夹v0.5.x。
https://github.com/zrysmt/DIY-zepto

参考阅读:
- Zepto源码分析-callbacks模块

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值