jQuery源码分析之Callbacks详解(9)

var Callbacks = function (option) {
        option = toString.call(option) === '[object Object]' ? option : {};
        //使用闭包,因为每个新建的callbacks都有自己的状态
        var list = [],      //回调列表
            _list = [],     //如果锁定这个callbacks对象,则清空list,将原list置入_list
            fired,          //是否执行过
            firingStart,    //当前回调函数列表执行的函数索引(起点)
            firingLength,   //回调函数的数组长度
            auto,   //标志是否自动执行,如果需要自动执行,则auto记忆着最后一次回调的参数(最后一次fire的参数),这是一个很诡异的且奇葩的用法
            //这个变量用法很诡异和犀利,既包含了是否指定执行的标志,又记录了数据
            //这个auto配合once简直就是丧心病狂:【第一次】执行了fire后才会自动执行,配合once可以做到:一次执行,后面不再追加和执行代码,保证了一组回调数据的稳定和安全
            stack = !option.once && [],     //一个callbacks栈,如果当前正在执行回调数组,而在执行中又新添了回调函数,那么把新的回调函数,那么新的回调函数都会压入该栈
            firing = false, //callbacks是否正在工作/执行
        //触发回调函数
            fire = function (data) {
                //注意这个data是个数组,如果配置了auto模式,那么auto永远不会为false,因为auto会是个数组
                auto = option.auto && data; //在这里,如果配置要求记忆最后的参数,则记忆这个参数(非常犀利的用法,直接取了数据)
                fired = true;
                firingIndex = firingStart || 0;
                firingStart = 0;//清空firingStart(不清空下次执行有出问题啦)
                firingLength = list.length;         //缓存list长度,外界可以访问
                firing = true; //正在执行回调函数
                for (; firingIndex < firingLength; firingIndex++) {
                    if (list[firingIndex].apply(data[0], data[1]) === false) {
                        //注意,如果配置了option.auto(自动执行),并且stack(栈)里存在函数,那么add()代码里有一段对于auto判定会直接执行本方法的代码
                        //我们要阻止掉那段代码,所以设置auto为false
                        auto = false;
                        break;
                    }//当函数返回false,终止执行后续队列
                }
                firing = false; //标志状态已经执行完毕回调函数[stack(栈)里面的函数尚未执行]
                //如果这个栈在没有配置once的情况下肯定是[],所以一定存在
                //这里主要作用是,如果没有配置once,则拦截下面的代码,如果配置了once,执行完代码清空数据
                if (stack) {
                    if (stack.length)//先把下面清空list状态的代码拦截掉,再判定是否有栈
                        fire(stack.shift()); //从栈头部取出,并递归fire()方法
                }
                else if (auto)    //代码走到这里,证明已经配置了option.once(只执行一次),于是把list清空
                    list = [];
                else                //证明没有配置auto,但是配置了once,那么祭出终极大法,直接废了这个callbacks对象
                    self.disable();
            };
        var self = {
            add: function () {//添加一个回调函数
                if (list) {
                    var start = list.length;
                    (function addCallback(args) {
                        each(args, function (item) {
                            if (isFunction(item)) {//是函数,则压入回调列表
                                list.push(item);
                                //注意typeof 和Object.prototype.toString是不一样的
                            } else if (toString.call(item) === '[object Array]') {//如果是个数组,则递归压入回调列表,这个判定抛弃了array-like
                                addCallback(item);
                            }
                        });
                    })(arguments);
                }
                if (firing)//如果当前正有回调函数在执行,那么需要更新当前回调函数列表的length,否则这个新压入的回调函数就会被掠过。
                    firingLength = list.length;
                else if (auto) {//如果当前没有执行回调函数,并且要求自动执行
                    //注意这里是给firingStart赋值,上面fire方法中正在使用的是firingIndex,这里不会影响到上面代码的执行线路
                    firingStart = start;
                    //执行我们新加入的小伙伴
                    fire(auto);
                }
                return this;
            },
            fire: function () {//触发回调函数
                self.fireWith(this, arguments);
                return this;
            },
            fireWith: function (context, args) {//触发回调函数,并指定上下文
                //如果配置了once,stack将为undefined,而once又需要保证只执行一次,所以一旦执行过一次,这里的代码不会再执行
                if (list && (!fired || stack)) {
                    //修正参数
                    //在这里,context索引为0
                    //而参数列表索引为2
                    //转换为数组访问是因为对象表示更加的消耗资源,在顶层的fire()代码中有auto[记忆参数,自动执行]这个功能,如果采用对象则开销了更大的内存
                    args = [context,
                        args ?
                        args.slice && args.slice()
                        || toSlice.call(args) :
                        []
                    ];
                    fire(args);
                }
                return this;
            },
            remove: function () {//移除一个回调函数
                if (list) {
                    each(arguments, function (item) {
                        var index;
                        //可能有多项,index可以在循环中表示检索的范围,之前检索的过的可以不用再检索
                        while ((index = inArray(item, list, index)) > -1) {
                            list.splice(index, 1);
                            if (firing) {
                                //保证上面fire中正在执行的函数列表能够正确运行,fire中设定全局这些变量为的就是这里可以异步移除
                                if (index <= firingLength)//修正长度
                                    firingLength--;
                                if (index <= firingLength)//修正索引
                                    firingIndex--;
                            }
                        }
                    });
                }
                return this;
            },
            has: function (fn) {//是否包含一个回调函数
                return fn ? inArray(fn, list) > -1 : list && list.length;
            },
            empty: function () {//清空这个callbacks对象
                list = [];
                firingLength = 0;
                return this;
            },
            disable: function () {//废掉这个callbacks对象,后续的回调函数列表不再执行
                list = stack = auto = undefined;
                return this;
            },
            disabled: function () {//是否已经废掉
                return !list; //转换为boolean
            },
            lock: function (isLock) {//锁定或解锁这个callbacks对象
                //无参,判断这个callbacks是否被锁定
                if (isLock == null) return !!_list;
                if (isLock) {//锁
                    _list = stack && list.concat(stack) || list;
                    list = undefined;
                } else {//解锁,jQuery并没有提供解锁功能,解锁让Callbacks变得不稳定
                    list = _list;
                    _list = undefined;
                }
                return this;
            },
            fired: function () {//这个callbacks是否执行过
                //转换为boolean,包括undefined,null,''等
                return !!fired;
            }
        };
        return self;
    };
    window.$ = window.$ || {};
    window.$.Callbacks = window.Callbacks = Callbacks;
}(window));

下载

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgfzyd.html