阅读 13

zepto源码Callback模块学习

Callback模块是用来管理回调函数,是作为Deferred对象的重要组成部分。

可选参数使用

可选参数: Options:

  • once: 是否最多执行一次,
  • memory: 是否记住最近的上下文和参数
  • stopOnFalse: 当某个回调函数返回false时中断执行
  • unique: 相同得回调只被添加一次 这是可选参数,下面可以进行试验:
var a = function (value) {
  console.log('a:' + value);
};
var b = function (value) {
  console.log('b:' + value);
};
var callbacks = $.Callbacks();
callbacks.add(a);
callbacks.fire(['hello']);
callbacks.add(b);
callbacks.fire('中');
复制代码

下面是他的输出结果:

a: hello,
a:中,
b:中
复制代码

可以看到得是,当我们第二次fire得时候,a函数也会执行。 在加入参数进行测试,首先加入memory

var callbacks = $.Callbacks({
  memory: true
});
callbacks.add(a);
callbacks.fire('hello');
callbacks.add(b);
输出:
a:hello,
b:hello
复制代码

加入memory参数,memory记录上一次触发回调函数的上下文和参数,之后的函数会使用这些参数执行。在来看once的使用

var callbacks = $.Callbacks({
  once: true
});
callbacks.add(a);
callbacks.fire('hello');
callbacks.fire('中');
输出:
a:hello
复制代码

可以看到的是,加入了once参数,虽然执行了两次fire方法,但是函数只执行了一次。

$.Callbacks = function(options) {
  options = $.extend({}, options)
  var memory, 
    fired, 
    firing,
    firingStart,
    firingLength,
    firingIndex,
    list = [],
    stack = !options.once && []
}
复制代码

再看看各个参数的意义

  • memory:会在记忆模式下记住上一次触发的上下文和参数
  • fired:代表回调是否已经触发过
  • firing:表示回调正在触发
  • firingStart:回调任务开始的位置
  • firingLength:回调数组的长度
  • firingIndex:当前回调的索引
  • list:表示真实的回调队列
  • stack:表示列表可以重复触发时,用来缓存触发过程中未执行的任务参数

fire函数

fire = function(data) {
  memory = options.memory && data
  fired = true
  firingIndex = firingStart || 0
  firingStart = 0
  firingLength = list.length
  firing = true
  for ( ; list && firingIndex < firingLength ; ++firingIndex ) {
    if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse) {
      memory = false
      break
    }
  }
  firing = false
  if (list) {
    if (stack) stack.length && fire(stack.shift())
    else if (memory) list.length = 0
    else Callbacks.disable()
  }
}
复制代码

fire函数是唯一内置的函数,他的作用是触发回调列表的函数。
首先看看他传进的参数,和咱们在外部调用$.Callbacksfire函数不一样。
这里的data是一个数组,data[0]表示上下文,data[1]是回调方法调用的参数数组。
接下来就是各个参数的的初始化,memory表示如果options.memorytrue,则保存data;回调列表触发状态firedtrue;如果firingStart不存在,那么firingIndex为0;firingStart置为0;回调列表长度firingLength设置为回调列表list的长度;回调列表的触发状态firing设置为true
然后遍历回调列表,逐个执行回调。如果某个回调函数的执行结果是false,并且stopOnFalsetrue,就把memory赋值为false,并且终止循环。
遍历完成后,将执行状态firing改为false。查看stack中有没有没有执行的任务,如果有,就将任务参数取出,使用fire函数执行。 如果memory存在,则清空list,但是不能设置list为undefined,因为后来可能还会添加回调任务。否则调用disable()方法,这个方法的主要作用是禁用回调任务的添加和执行。

Callbacks

最终这个文件返回的是Callbacks对象,我们来看看他的具体实现:

Callbacks = {
  add: function () {
    if (list) {
      var start = list.length,
        add = function (args) {
          $.each(args, funciton(_, arg) {
            if (typeof arg === 'function') {
              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;
        fire(memory)
      }
    }
    return this;
  }
}
复制代码

这个函数主要的作用就是往回调任务列表list中添加回调任务。首先判断list是否存在,这里即使是空数组也是会进入到这个逻辑中,如果存在,开始位置start赋值为list的长度。内部有一个add方法,就是将调用Callbacks.add()传入的参数,添加到回调任务列表list中。内部在判断的时候会考虑到参数可能是函数,也可能是数组,如果是函数就直接往list中添加该函数,如果是数组,就再次调用本方法。 然后就是调用add方法,把参数传进去。如果回调正在进行,则修正回调任务的长度firingLength为当前任务列表的长度,以便后续添加的回调函数可以执行。如果是记忆模式,回调列表的开始位置firingStart就设置为start,然后调用fire,参数为缓存的上下文以及参数。最后返回Callbacks对象,以便链式调用。

fireWith

fireWith: function(context, args) {
  if (list && (!fired || stack)) {
    args = args || [];
    args = [context, args.slice ? args.slice() : args];
    if (firing) stack.push(args);
    else fire(args);
  }
  return this;
}
复制代码

参数的含义是:

  • context:执行的上下文
  • args: 参数数组 如果回调列表list存在,并且回调列表没有被执行过或者stack存在,进入if。首先判断args是否存在,如果不存在则赋值为空数组,然后对args重新调整,第一个参数是上下文,第二个看是不是args是不是数组,如果是数组,拷贝它。如果回调正处于触发的状态,则把上下文和参数存储在stack中,从后面可以知道,当回调函数执行完毕后,会从stack中把args取出,然后调用fire

disable

disable: function () {
  list = stack = memory = undefined;
  return this;
}
复制代码

disable的作用就是禁用回调,就是把liststackmemory都置为undefined,调用disable以后,addremovefirefireWith都不能生效,因为他们生效的条件都是list存在。

lock

lock: function () {
  stack = undefined;
  if (!memory) Callbacks.disable();
  return this;
}
复制代码

disable不同的是,如果我们设置memorytrue,它才会调用disable方法。一般情况,即使我们调用了lock方法,但是我们仍然能够向回调列表中添加函数,添加完毕后能够用memory的上下文和参数触发回调函数。

remove

remove: function () {
  if (list) {
    $.each(arguments, function(_, arg){
      var index
      while ((index = $.inArray(arg, list, index)) > -1) {
        list.splice(index, 1);
        if (firing) {
          if (index <= firingLength) --firingLength;
          if (index <= firingIndex) --firingIndex;
        }
      }
    }) 
  }
}
复制代码

上面是remove方法,先决条件都是需要list存在,遍历传进来的参数,如果在回调列表中arg的索引大于-1,即回调列表中存在该回调,就从回调列表中删除此函数。如果回调正在执行就是firingtrue的时候,修正firingLengthfiringIndex以免找不到回调函数。

关注下面的标签,发现更多相似文章
评论