JQuery之Callback源码分析

主要对JQuery的Callback部分进行讲解

Callback源代码

var optionsCache = {};

//主要作用是  once memory unique  stopOnFalse
//once: 确保回调队列中的回调函数被执行一次  Deferred会用到
//memory:保留上一次执行的值,那怕后续添加一个回调函数,也确保调用上一次记录的值为参数  Deferred会用到
//unique:确保同一个函数仅被添加一次
//stopOnFalse:如果回调函数队列中有一个回调函数执行后返回false就立即中断剩下的回调函数执行
function createOptions(options) {
    var object = optionsCache[options] = {};
    //core_rnotwhite = /\S+/g,
    //var core_rnotwhite = /\S+/g; "one test ".match(core_rnotwhite)
    //["one", "test"]
    jQuery.each(options.match(core_rnotwhite) || [], function (_, flag) {
        object[flag] = true;
    });
    return object;
}

//主体部分
//比如传入 "once memory"
//optionsCache["once memory"]={},optionsCache["once memory"]["once"]=true,optionsCache["once memory"]["memory"]=true;
jQuery.Callbacks = function (options) {
    options = typeof options === "string" ?
        (optionsCache[options] || createOptions(options)) :
        jQuery.extend({}, options);

    var
        firing,//标志位,如果队列正在执行 flag to know if list is currently firing
        memory,//最后执行的值 last fire value
        fired,//标志位,如果队列已经执行 flag to know if list was already fired
        firingLength,//执行时,整个队列长度 end of the loop when firing
        firingIndex,//回调队列正在执行的当前函数的队列索引 index of currently firing callback
        firingStart,//真正执行回调队列中 的回调函数在队列中的起始位置 first callback to fire
        list = [],//实际的回调队列 actual callback list
        stack = !options.once && [],//栈保存可以多次被执行的队列
        //stack : 当"once memory"的时候,stack=false;当"memory"的时候,stack=[];这个主要是针对once来说的
        fire = function (data) {
            //初始化标志信息
            memory = options.memory && data; //&&短操作符,如果碰到false(可以转化为false的)就去那个值,如果没有碰到就去最后一个
            fired = true;
            firingIndex = firingStart || 0; //||短操作符,如果碰到true(可以转化为true的)就取那个值,取第一个为true的值,否则也是取最后一个
            firingStart = 0;
            firingLength = list.length;
            firing = true;
            for (; list && firingIndex < firingLength; firingIndex++) {
                //队列中的函数执行,看回调是否阻止当执行的函数返回false的时候,是否停止对后续回调的调用   当有阻止模式时
                if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse) {
                    memory = false;
                    break;
                }
            }
            firing = false;
            //执行完回调函数后,对后续队列进行处理,主要是针对几种模式,分别处理
            if (list) {
                if (stack) {//当栈中有数据时,即是非 once模式时
                    if (stack.length) {
                        fire(stack.shift());
                    }
                } else if (memory) {//当有记忆模式时
                    list = [];
                } else { //其他情况
                    self.disable();
                }
            }
        },
        //真实的callback对象
        slef = {
            //添加一个callback 或者 callback 集合到 回调队列中去
            add: function () {
                if (list) {
                    //我们保持当前长度
                    var start = list.length;
                    //立即执行第一次调用
                    (function add(args) {
                        jQuery.each(args, function (_, arg) {
                            var type = jQuery.type(arg);//判断arguments数组中单个元素的类型
                            if (type === "function") {
                                //当为非唯一模式和队列没有arg回调函数时
                                if (!options.unique || !self.has(arg)) {
                                    list.push(arg);
                                }
                            } else if (arg && arg.length && type !== "string") {//当arg为数组并且不是字符型,即为[]类型
                                add(arg); //递归添加
                            }
                        });
                    })(arguments);

                    //针对模式后续处理
                    if (firing) {
                        firingLength = list.length;
                    } else if (memory) { //当有记忆模式时候,后续添加的函数会被立即调用,使用上一次调用的参数
                        firingStart = start;
                        fire(memory);
                    }
                }
                return list;
            },
            remove: function () {
                if (list) {
                    jQuery.each(arguments, function (_, arg) {
                        var index;
                        //从0检索arg是否在list中
                        while ((index = jQuery.inArray(arg, list, index)) > -1) {
                            list.splice(index, 1);
                            //调整firing的长度和索引
                            if (firing) {
                                if (index <= firingLength) {
                                    firingLength--;
                                }
                                if (index <= firingIndex) {
                                    firingIndex--;
                                }
                            }
                        }
                    });
                }
                return this;
            },
            //函数是否在回调队列中 
            has: function (fn) {
                return fn ? jQuery.inArray(fn, list) > -1 : !!(list && list.length);
            },
            //清空队列
            empty: function () {
                list = [];
                firingLength = 0;
                return this;
            },
            //使队列不够做任何事情了,已经undefined了
            disable: function () {
                list = stack = memory = undefined;
                return this;
            },
            //是否是可用状态
            disabled: function () {
                return !list;
            },
            //锁定栈为他的当前状态
            lock: function () {
                stack = undefined;
                if (!memory) {//当为记忆模式时候
                    self.disable();
                }
                return this;
            },
            //栈的当前状态
            locked: function () {
                return !stack;
            },
            //调用回调集合并且传递上下文和参数信息过去
            fireWith: function (context, args) {
                args = args || [];
                //args为数组的时候就让其为数组,当不为数组的时候把它变为数组
                args = [context, args.slice ? args.slice() : args];
                if (list && (!fired || stack)) {
                    if (firing) {//当正在执行时,把arguments推到栈中去
                        stack.push(args);
                    } else {//当为非执行状态的时候,就立马执行调用队列中的回调,并且把回调参数传递进去
                        // 这里的fire是调用上面的fire,而非下面的fire
                        //因为fire还在后面定义的
                        //所以下面的fire是对fireWith不可见的
                        fire(args);
                    }
                }
                return this;
            },
            //调用回调使用调用给的参数
            fire: function () {
                slef.fireWith(this, arguments);
                return this;
            },
            //回调是否已经被调用过了
            fired: function () {
                return !!fired;
            }
        };
    return self;
}

使用方式见

JQuery官方Callback使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值