欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

jquery源码解读之callbacks篇

程序员文章站 2022-07-13 12:35:01
...

项目地址:es6-jquery

一、callbacks模块作用  

callbacks模块通过维护一个回调函数列表,来实现对回调函数地管理。其操作包括增加,触发,移除,清空,禁用,锁定等,是jquery.Defrred, jquery.ajax, jquery.ready等依赖的底层模块。

二、代码示例

let cbList = $.callbacks('once memory');
let fn = (args) => {
    alert(String(args));
}
cbList.add( fn);
cbList.fire('will fire');

三、API 详解

  1. add(fn || fnCollections)

增加一个或多个回调函数到内部回调函数列表中

  1. fire(args)

将给定的参数挨个传进回调函数列表中的函数,并执行
回调函数执行的上下文是当前callbacks

  1. fireWith(context, args)

将给定的参数挨个传进回调函数列表中的函数,并执行
回调函数执行的上下文是当前传入的context

  1. disable

禁用任何添加和触发操作,删除回调函数列表,清空记忆值,清空参数记忆数组,终止回调函数中所有操作

  1. lock

禁用callbacks的触发操作
如果当前不是memory模式且回调函数列表并没有在执行,则相当于disable操作
否则,只会禁用触发操作,后续的添加操作不会受影响

  1. remove(fn)

从回调函数列表中移除fn

  1. empty()

清空回调函数列表

  1. has(fn)

判断fn是否在当前回调函数列表中,如果fn为空,则判断当前回调函数列表是否为空

  1. locked()

判断当前回调函数列表是否处于锁定状态

  1. disabled()

返回当前回调函数列表的禁用状态

四、源码分析

  1. 整体结构
/*
  1. 工具函数
  2. 将以空格为分隔的字符串转为对象
*/
function createOptions(options) {
}
let callbacks = function (options) {
   //  获取当前传入的options值
   options = createOptions(options) || {};
   /*
    定义下面函数需要的各种变量
    +. list  回调函数列表
    +. firing 当前回调函数是否在执行
    +. fired  当前回调函数列表是否至少已经执行过一次
    +. locked 当前回调函数列表是否被锁定
    +. disabled 当前回调函数列表是否被禁用
    +. queue 存储回调函数列表触发时传入的参数,是个二维数组,每次传入[context, args]
    +. memory 这个值标示queue的值如何处理,值存在多种情况
       > + options.memory不存在,memory为false
       > + 回调函数执行返回false且存在stopOnFalse标示时
       > + 禁用状态下 memory值为空字符串
       > + 锁定状态下,如果不存在memory标示,且回调函数列表并没有在执行,此时memory为空字符串
      > + 其它情况 memory = [context, args], 用来获取queue中存储的记忆参数
*/
 // 定义一个对象用来存储callbacks的各种方法,如下
   let self = {
      add: function () {},
      fire: function () {},
      fireWith: function () {},
      remove: function () {},
      empty: function () {},
      has: function () {},
      lock: function () {},
      disable: function () {},
      disabled: function () {}
   }
   return self;
}
  1. 核心工具函数

     fire 具体触发过程

  • 状态设置
/* 
  设置当前回调列表的锁定状态
  表示当前回调列表已经触发
*/
locked = locked || options.once;
fired = firing = true;
  • 触发
// 当数组长度大于0时执行for循环体,如果数组长度小于等于0,将fireingIndex重置为-1
for(; queue.length; firingIndex = -1 ) {
    // 从queue中取出第一个值并将数组的长度减一
    memory = queue.shift();
    // ++firingIndex相当于从list[0]开始执行
    while (++firingIndex < list.length) {
        // 如果回调函数执行返回false,会根据传入的标识进行处理
        if (list[firingIndex].apply(memory[0], memory[1]) === false 
            && options.stopOnFalse) {
            // 如果传入的标识含有stopOnFalse,那么会将memory设置为false
            // 同时将firingIndex 设置为数组最后一位
            memory = false;
            firingIndex = list.length -1;
        }
    }
}
  • 触发之后,对回调函数列表进行设置
if (!options.memory) {
    memory = false;
}
if (locked) {
    if (memory) {
        list = [];
    } else {
        list = ''; 
    }
} 
  1. 函数详解

      (1) add 增加一个或者多个函数到回调函数列表中

   如果是在memory模式下,增加的函数会立即执行,其执行的上下文为memory[0],执行的参数是memory[1]
  • 追加函数到函数列表之前
/* 
 如果在memory模式下且回调函数列表没有被触发,则设定正在触发的回调函数索引为
 回调函数列表长度减一,并将上一次触发的参数存入queue
*/
 if (memory && !firing) {
    firingIndex = list.length - 1;
    queue.push(memory);
 }
  • 追加函数到函数列表
   // 使用一个立即执行函数来进行函数追加
  (function (args) {
    // 分两种情况
    // 1. 如果args是函数,当前是非唯一模式,则直接添加;如果是唯一模式,
    // 则判断args是否存在在当前函数列表中,如果不存在就添加它
    // 2. 如果args是数组,则继续递归调用add进行深层遍历
   })(arguments)
  • 追加函数到函数列表之后
 // 如果在memory模式下且当前回调函数列表没有在执行,就调用fire方法
 if (memory && !firing) {
     fire();
 }

(2) fireWith

  • 参数收集
let fireWith: function (context, args) {
    if (!locked) {
        // 如果回调函数列表不是处于锁定或禁用状态
        // 收集参数
        let args = args || [];
        args = [context, args.slice ? args.slice : []];
        if (queue) {
            // 如果queue存在,则将收集的参数数组传入queue
            queue.push([ context, args);
        }
        // 如果当前回调列表没有正在执行,则触发回调函数列表执行
        if (!firing) {
            fire();
        }
    }
    return this;
}

(3) fire(args)

  • 直接在自身作用域环境下触发
 // fire实际上是fireWith调用的一种特殊情况,
 // 此时context为回调管理对象本身

 let fire = function (args) {
     fireWith(this, args);
     return this;
 }

(4) lock

/*
        1.设置locked和queue等于空数组
        2. 这里有个疑问为什么不直接设置locked为true呢,其实空数组就是等于true
        3. 如果memory为false或者为空,且当前回调函数列表尚没有执行,则彻底删除
        回调函数列表, 设置memory等于空字符串。
    */
    
    locked = queue = [];
    if (!memory && !firing) {
        list = memory = '';
    }
    return this;

(5) disable()

  locked = queue;
  list = memory = '';
  return this;

(6) locked()

return !!locked

(7) disabled()

/*
判断列表是否禁用
*/
return !list

(8) empty()

    // 清空列表
    if (list) {
        list = [];
    }

(9) has(fn)

// 判断函数是否在回调函数列表,如果fn为空,则判断该函数列表是否长度大于0
// $.inArray(data, list, index) 返回某个数据在列表中的位置 类似于indexOf
return fn ? $.inArray(fn, list) > -1 ? list.length > 0;

(10) remove(fn)

    let remove = function (...fns) {
        let index = null;
        // 遍历参数
        $.each(fns, function(_, fn) {
            while (index = ($.inArray(fn, list, index)) > -1 ) {
                list.splice(index, 1);
                if (index <= firingIndex) {
                    firingIndex--;
                } 
            }
        })
    }

转载于:https://www.cnblogs.com/fecode/articles/6835534.html