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

jQuery源码学习之Callbacks()

程序员文章站 2022-03-29 10:44:01
jquery.callbacks()是在版本1.7中新加入的。它是一个多用途的回调函数列表对象,提供了一种强大的方法来管理回调函数队列。 1、使用场景: var callbacks = $.ca...

jquery.callbacks()是在版本1.7中新加入的。它是一个多用途的回调函数列表对象,提供了一种强大的方法来管理回调函数队列。

1、使用场景:

var callbacks = $.callbacks();

  callbacks.add(function() {
    alert('a');
  })

  callbacks.add(function() {
    alert('b');
  })

  callbacks.fire(); //输出结果: 'a' 'b'

便捷的处理参数

once: 确保这个回调列表只执行( .fire() )一次(像一个递延 deferred).

memory: 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 deferred).

unique: 确保一次只能添加一个回调(所以在列表中没有重复的回调).

stoponfalse: 当一个回调返回false 时中断调用

例如:

var callbacks = $.callbacks('once');

  callbacks.add(function() {
    alert('a');
  })

  callbacks.add(function() {
    alert('b');
  })

  callbacks.fire(); //输出结果: 'a' 'b'
  callbacks.fire(); //未执行

2、jquery.callbacks()的api:

callbacks.add()        回调列表中添加一个回调或回调的集合。
callbacks.disable()    禁用回调列表中的回调
callbacks.disabled()   确定回调列表是否已被禁用。 
callbacks.empty()      从列表中删除所有的回调.
callbacks.fire()       用给定的参数调用所有的回调
callbacks.fired()      访问给定的上下文和参数列表中的所有回调。 
callbacks.firewith()   访问给定的上下文和参数列表中的所有回调。
callbacks.has()        确定列表中是否提供一个回调
callbacks.lock()       锁定当前状态的回调列表。
callbacks.locked()     确定回调列表是否已被锁定。
callbacks.remove()     从回调列表中的删除一个回调或回调集合。

分析:

jquery.callbacks = function(options) {

    // convert options from string-formatted to object-formatted if needed
    // (we check in cache first)
    //通过字符串在optionscache寻找有没有相应缓存,如果没有则创建一个,有则引用
    //如果是对象则通过jquery.extend深复制后赋给options。
    options = typeof options === "string" ?
        (optionscache[options] || createoptions(options)) :
        jquery.extend({}, options);

    var // last fire value (for non-forgettable lists)
    memory, // 最后一次触发回调时传的参数

        // flag to know if list was already fired
        fired, // 列表中的函数是否已经回调至少一次

        // flag to know if list is currently firing
        firing, // 列表中的函数是否正在回调中

        // first callback to fire (used internally by add and firewith)
        firingstart, // 回调的起点

        // end of the loop when firing
        firinglength, // 回调时的循环结尾

        // index of currently firing callback (modified by remove if needed)
        firingindex, // 当前正在回调的函数索引

        // actual callback list
        list = [], // 回调函数列表

        // stack of fire calls for repeatable lists
        stack = !options.once && [], // 可重复的回调函数堆栈,用于控制触发回调时的参数列表

        // fire callbacks// 触发回调函数列表
        fire = function(data) {
            //如果参数memory为true,则记录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) {
                    // 阻止未来可能由于add所产生的回调
                    memory = false; // to prevent further calls using add
                    break; //由于参数stoponfalse为true,所以当有回调函数返回值为false时退出循环
                }
            }
            //标记回调结束
            firing = false;
            if (list) {
                if (stack) {
                    if (stack.length) {
                        //从堆栈头部取出,递归fire
                        fire(stack.shift());
                    }
                } else if (memory) { //否则,如果有记忆
                    list = [];
                } else { //再否则阻止回调列表中的回调
                    self.disable();
                }
            }
        },
        // actual callbacks object
        // 暴露在外的callbacks对象,对外接口
        self = {
            // add a callback or a collection of callbacks to the list
            add: function() { // 回调列表中添加一个回调或回调的集合。
                if (list) {
                    // first, we save the current length
                    //首先我们存储当前列表长度
                    var start = list.length;
                    (function add(args) { //jquery.each,对args传进来的列表的每一个对象执行操作
                        jquery.each(args, function(_, arg) {
                            var type = jquery.type(arg);
                            if (type === "function") {
                                if (!options.unique || !self.has(arg)) { //确保是否可以重复
                                    list.push(arg);
                                }
                                //如果是类数组或对象,递归
                            } else if (arg && arg.length && type !== "string") {
                                // inspect recursively
                                add(arg);
                            }
                        });
                    })(arguments);
                    // do we need to add the callbacks to the
                    // current firing batch?
                    // 如果回调列表中的回调正在执行时,其中的一个回调函数执行了callbacks.add操作
                    // 上句话可以简称:如果在执行callbacks.add操作的状态为firing时
                    // 那么需要更新firinglength值
                    if (firing) {
                        firinglength = list.length;
                        // with memory, if we're not firing then
                        // we should call right away
                    } else if (memory) {
                        //如果options.memory为true,则将memory做为参数,应用最近增加的回调函数
                        firingstart = start;
                        fire(memory);
                    }
                }
                return this;
            },
            // remove a callback from the list
            // 从函数列表中删除函数(集)
            remove: function() {
                if (list) {
                    jquery.each(arguments, function(_, arg) {
                        var index;
                        // while循环的意义在于借助于强大的jquery.inarray删除函数列表中相同的函数引用(没有设置unique的情况)
                        // jquery.inarray将每次返回查找到的元素的index作为自己的第三个参数继续进行查找,直到函数列表的尽头
                        // splice删除数组元素,修改数组的结构
                        while ((index = jquery.inarray(arg, list, index)) > -1) {
                            list.splice(index, 1);
                            // handle firing indexes
                            // 在函数列表处于firing状态时,最主要的就是维护firinglength和firgingindex这两个值
                            // 保证fire时函数列表中的函数能够被正确执行(fire中的for循环需要这两个值
                            if (firing) {
                                if (index <= firinglength) {
                                    firinglength--;
                                }
                                if (index <= firingindex) {
                                    firingindex--;
                                }
                            }
                        }
                    });
                }
                return this;
            },
            // check if a given callback is in the list.
            // if no argument is given, return whether or not list has callbacks attached
            // 回调函数是否在列表中.
            has: function(fn) {
                return fn ? jquery.inarray(fn, list) > -1 : !! (list && list.length);
            },
            // remove all callbacks from the list
            // 从列表中删除所有回调函数
            empty: function() {
                list = [];
                firinglength = 0;
                return this;
            },
            // have the list do nothing anymore
            // 禁用回调列表中的回调。
            disable: function() {
                list = stack = memory = undefined;
                return this;
            },
            // is it disabled?
            //  列表中否被禁用
            disabled: function() {
                return !list;
            },
            // lock the list in its current state
            // 锁定列表
            lock: function() {
                stack = undefined;
                if (!memory) {
                    self.disable();
                }
                return this;
            },
            // is it locked?
            // 列表是否被锁
            locked: function() {
                return !stack;
            },
            // call all callbacks with the given context and arguments
            // 以给定的上下文和参数调用所有回调函数
            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;
            },
            // call all the callbacks with the given arguments
            // 以给定的参数调用所有回调函数
            fire: function() {
                self.firewith(this, arguments);
                return this;
            },
            // to know if the callbacks have already been called at least once
            // // 回调函数列表是否至少被调用一次
            fired: function() {
                return !!fired;
            }
        };
    return self;
};

jquery.callbacks()的核心思想是 pub/sub 模式,建立了程序间的松散耦合和高效通信。

pub/sub (观察者模式) 的背后,总的想法是在应用程序中增强松耦合性。并非是在其它对象的方法上的单个对象调用。一个对象作为特定任务或是另一对象的活动的观察者,并且在这个任务或活动发生时,通知观察者。观察者也被叫作订阅者(subscriber),它指向被观察的对象,既被观察者(publisher 或 subject)。当事件发生时,被观察者(publisher)就会通知观察者(subscriber)。