jquery源码 Callback

工具方法。对函数的统一管理。

jquery2.0.3版本$.Callback()部分的源码如下:

// String to Object options format cache
var optionsCache = {};

// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
    var object = optionsCache[ options ] = {};
    jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
        object[ flag ] = true;
    });
    return object;
}

/*
 * Create a callback list using the following parameters:
 *
 *    options: an optional list of space-separated options that will change how
 *            the callback list behaves or a more traditional option object
 *
 * By default a callback list will act like an event callback list and can be
 * "fired" multiple times.
 *
 * Possible options:
 *
 *    once:            will ensure the callback list can only be fired once (like a Deferred)
 *
 *    memory:            will keep track of previous values and will call any callback added
 *                    after the list has been fired right away with the latest "memorized"
 *                    values (like a Deferred)
 *
 *    unique:            will ensure a callback can only be added once (no duplicate in the list)
 *
 *    stopOnFalse:    interrupt callings when a callback returns false
 *
 */
jQuery.Callbacks = function( options ) {

    // Convert options from String-formatted to Object-formatted if needed
    // (we check in cache first)
    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 = 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; // To prevent further calls using add
                    break;
                }
            }
            firing = false;
            if ( list ) {
                if ( stack ) {
                    if ( stack.length ) {
                        fire( stack.shift() );
                    }
                } else if ( memory ) {
                    list = [];
                } else {
                    self.disable();
                }
            }
        },
        // Actual Callbacks object
        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, 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?
                    if ( firing ) {
                        firingLength = list.length;
                    // With memory, if we‘re not firing then
                    // we should call right away
                    } else if ( 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( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                            list.splice( index, 1 );
                            // Handle firing indexes
                            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;
};

一、$.Callback()的简单使用及应用场景

1、$.Callback()的用法。

观察者模式,添加完后统一触发。

       function aaa(){
            alert(1);
        }
        function bbb(){
            alert(2);
        }
        var cb= $.Callbacks();
        cb.add(aaa);
        cb.add(bbb);
        cb.fire();    

2、好处,应用场景。

要统一的管理aaa和bbb。有时候如下,很难对不同作用域下的函数进行统一管理。

  function aaa(){
            alert(1);
        }
        (function(){
            function bbb(){
                alert(2);
            }
        })();
        aaa();
        bbb();

只能弹出1,因为bbb是局部作用域中的。

$callback可以做到。如下,只要cb是全局的。

 var cb= $.Callbacks();
        function aaa(){
            alert(1);
        }
        cb.add(aaa);
        (function(){
            function bbb(){
                alert(2);
            }
            cb.add(bbb);
        })();
        cb.fire();

对应复杂情况很有用。统一管理,通过fire统一触发。

二、原理图

Callback接收一个参数,可以有4个选项,once,memory,unique,stopOnFalse。

self单体有这些方法:add,remove,has,empty,disable,disabled,lock,locked, fireWith,fire,fired。

list=[]数组变量,用来收集回调函数。fire的时候对其循环调用。

add:push数组

fire:调用fireWith,fireWith允许传参,fire可传可不传。

fireWith:调用私有函数fire,在私有函数fire中for循环list。

remove:splice数组。

4个参数:

  • once针对fire()只循环一次
  • memory 针对add,作用到add上,add时判断有memory就去执行fire。
  • unique 针对add,添加的时候就可以去重
  • stopOnFalse 针对fire,在for循环时遇到false,立即跳出循环

三、更多用法

1、callback4个参数的作用

不传参数,fire几次就触发几次。

function aaa() {
            alert(1);
        }

        function bbb() {
            alert(2);
        }
        var cb = $.Callbacks();
        cb.add(aaa);
        cb.add(bbb);
        cb.fire(); //1 2
        cb.fire();//1 2

  • once:fire只能触发一次
        function aaa() {
            alert(1);
        }

        function bbb() {
            alert(2);
        }
        var cb = $.Callbacks(‘once‘);
        cb.add(aaa);
        cb.add(bbb);
        cb.fire(); //1 2
        cb.fire();

不传参数,在fire之后add的回调不能被fire。

//不写参数,只弹出1,2不会弹出
 function aaa() {
            alert(1);
        }

        function bbb() {
            alert(2);
        }
        var cb = $.Callbacks();
        cb.add(aaa);
        cb.fire(); //1
        cb.add(bbb);

  • memory记忆,在fire前面后面add的方法都能得到执行。
function aaa() {
            alert(1);
        }

        function bbb() {
            alert(2);
        }
        var cb = $.Callbacks(‘memory‘);
        cb.add(aaa);
        cb.fire(); //1 2
        cb.add(bbb);
  • unique:去重

//不加参数,add2次aaa,就会触发2次aaa
function aaa() {
            alert(1);
        }

        var cb = $.Callbacks();
        cb.add(aaa);
        cb.add(aaa);
        cb.fire(); //1 1

function aaa() {
            alert(1);
        }

        var cb = $.Callbacks(‘unique‘);
        cb.add(aaa);
        cb.add(aaa);
        cb.fire(); //1 加了unique参数,同样的函数不能多次add
  • stopOnFalse:函数返回false跳出循环
function aaa() {
            alert(1);
            return false;
        }
        function bbb() {
            alert(2);
        }

        var cb = $.Callbacks();
        cb.add(aaa);
        cb.add(bbb);
        cb.fire(); //1 2 不传参,第一个函数返回false时后面的函数也能正常执行
function aaa() {
            alert(1);
            return false;
        }
        function bbb() {
            alert(2);
        }

        var cb = $.Callbacks(‘stopOnFalse‘);
        cb.add(aaa);
        cb.add(bbb);
        cb.fire(); //1
        //传参stopOnFalse,第一个函数返回false时后面的函数不再执行

2、callback也可以接收组合的形式

 function aaa() {
            alert(1);
        }
        function bbb() {
            alert(2);
        }
        //组合使用,只执行一次,并且弹出1 2
        var cb = $.Callbacks(‘once memory‘);
        cb.add(aaa);
        cb.fire(); //1
        cb.add(bbb);
        cb.fire();

源码中:
传入了 once和memory后,

options={once:true,memory:true}
optionCache={

"once memory":{once:true,memory:true}
}

6、fire()可以传参

参数作为回调的实参

function aaa(n) {
            alert("aaa "+n);
        }
        function bbb(n) {
            alert("bbb "+n);
        }
        var cb = $.Callbacks();
        cb.add(aaa);
        cb.add(bbb);
        //fire传参
        cb.fire("hello"); //弹出aaa hello 和bbb hello

四、源码

Callbacks就是一个工具函数,内部定义了一个self ,add和remove还有has等挂在self上。

1、add源码

主要是把回调函数Push到数组list中。

       add: function() {
            if ( list ) { //list初始化为[],if判断会返回true
                // First, we save the current length
                var start = list.length;
                (function add( args ) {
                    jQuery.each( args, function( _, arg ) { ////处理cb.add(aaa,bbb)这种调用
                        var type = jQuery.type( arg );//arg就是每一个函数
                        if ( type === "function" ) {//arg是函数就push到list中,此时有个判断有没有unique
                            if ( !options.unique || !self.has( arg ) ) {//有unique走后面,判断list中有没有这个函数,有就不添加了
                                list.push( arg );
                            }
                        } else if ( arg && arg.length && type !== "string" ) { //处理cb.add([aaa,bbb])这种调用
                            // Inspect recursively
                            add( arg );//递归分解,最终还是push到list
                        }
                    });
                })( arguments );
                // Do we need to add the callbacks to the
                // current firing batch?
                if ( firing ) {
                    firingLength = list.length;
                    // With memory, if we‘re not firing then
                    // we should call right away
                } else if ( memory ) {
                    firingStart = start;
                    fire( memory );
                }
            }
            return this;
        },

2、remove源码

// Remove a callback from the list
        remove: function() {
            if ( list ) {
                jQuery.each( arguments, function( _, arg ) {
                    var index;
                    while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
                        list.splice( index, 1 );//主要就是splice删除操作
                        // Handle firing indexes
                        if ( firing ) {
                            if ( index <= firingLength ) {
                                firingLength--;
                            }
                            if ( index <= firingIndex ) {
                                firingIndex--;
                            }
                        }
                    }
                });
            }
            return this;
        },

3、fire源码

1、整体调用逻辑

self的fire调用self的fireWith,fireWith把参数传递到fire()函数。

// 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;
            },

fire()时主要是for循环

 // Fire callbacks
        fire = function( data ) {
            memory = options.memory && data;
            fired = true;//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 ) {//每次函数调用同时处理stopOnFalse的情况
                    memory = false; // To prevent further calls using add //stopOnFalse后有memory也不好使了
                    break;
                }
            }
            firing = false;//触发结束
            if ( list ) {
                if ( stack ) {
                    if ( stack.length ) {
                        fire( stack.shift() );
                    }
                } else if ( memory ) {
                    list = [];
                } else {
                    self.disable();
                }
            }
        },

2、firing特殊情况

线通过例子来看一下效果

        function aaa() {
            alert(1);
            cb.fire(); //在这里调用fire()会出现什么问题 死循环
        }
        function bbb() {
            alert(2);
        }
        var cb = $.Callbacks();
        cb.add(aaa);
        cb.add(bbb);

        cb.fire(); 

在执行函数的过程中再次调用fire()的执行顺序是怎样的?

var bBtn=true;//用bBtn避免死循环
        function aaa() {
            alert(1);
            if(bBtn){
                cb.fire();//注意这里fire调用后执行顺序是1 2 1 2,而不是1 1 2 2
                bBtn=false;
            }

        }
        function bbb() {
            alert(2);
        }
        var cb = $.Callbacks();
        cb.add(aaa);
        cb.add(bbb);

        cb.fire();

结论:把函数运行过程中触发的fire()放到了运行过程的队列当中。

怎么做到的,看源码:

在fireWith的时候判断for循环有没有执行完

 fireWith: function( context, args ) {
            if ( list && ( !fired || stack ) ) {
                args = args || [];
                args = [ context, args.slice ? args.slice() : args ];
                if ( firing ) {//firing在for循环没有走完时一直是true
                    stack.push( args );//所以这句话意思就是函数执行时再去fire()调用就会push到stack数组中
                } else {
                    fire( args );
                }
            }
            return this;
        },

再去调用fire()的时候

  // Fire callbacks
        fire = function( data ) {
            memory = options.memory && data;
            fired = true;//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 ) {//每次函数调用同时处理stopOnFalse的情况
                    memory = false; // To prevent further calls using add //stopOnFalse后有memory也不好使了
                    break;
                }
            }
            firing = false;//触发结束
            if ( list ) {
                if ( stack ) {        //这就是出现在函数执行过程中再次fire()的时候,等循环执行完,再去按顺序执行
                    if ( stack.length ) {
                        fire( stack.shift() );
                    }
                } else if ( memory ) {//只执行一次的时候,有once,memory就清空list,此时fire()就相当于一个执行一个空数组
                    list = [];
                } else {
                    self.disable();//disable阻止后续任何的fire()操作
                }
            }
        },

针对下面这段源码的一个例子:once和memory同时存在的时候,fire()无效因为list为[]了,但是add仍然有效。

else if ( memory ) {//只执行一次的时候,有once,memory就清空list,此时fire()就相当于一个执行一个空数组
                    list = [];
                } else {
                    self.disable();//disable阻止后续任何的fire()操作
                }

disable阻止后续任何的fire()操作。

 function aaa() {
            alert(1);
        }
        function bbb() {
            alert(2);
        }
        //组合使用,只执行一次,并且弹出1 2 3
        var cb = $.Callbacks(‘once memory‘);
        cb.add(aaa);
        cb.fire(); //1
        cb.fire();//此时list为[]
        cb.add(bbb);
        cb.fire();
        function ccc(){
            alert(3);
        }
        cb.add(ccc);

4、其他源码

has(fn):判断list有没有fn

empty: 清空数组list=[]

disable:全部锁住,禁止了,如下

// Have the list do nothing anymore
            disable: function() {
                list = stack = memory = undefined;
                return this;
            },

disabled:判断是不是禁止了。return !list;

lock:只是把stack锁住

// Lock the list in its current state
            lock: function() {
                stack = undefined;
                if ( !memory ) {
                    self.disable();
                }
                return this;
            },

locked:是否locked。 return !stack;

5、 lock和disable的区别

disable禁止所有操作

 function aaa() {
            alert(1);
        }
        function bbb() {
            alert(2);
        }

        var cb = $.Callbacks(‘memory‘);
        cb.add(aaa);
        cb.fire(); //1
        cb.disable();//disable()后只能弹出1 因为禁止所有操作了,虽然有Memory
        cb.add(bbb);//不起作用了,此时list变为undefined了
        cb.fire();//不起作用了

lock只是锁住数组

function aaa() {
            alert(1);
        }
        function bbb() {
            alert(2);
        }

        var cb = $.Callbacks(‘memory‘);
        cb.add(aaa);
        cb.fire(); //1 2
        cb.lock();//lock()只是把后续的fire()锁住,其他操作是锁不住的
        cb.add(bbb);
        cb.fire();//不起作用了 此时list为[]

本文作者starof,因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/6885500.html有问题欢迎与我讨论,共同进步。

时间: 2024-12-16 12:01:28

jquery源码 Callback的相关文章

jQuery源码分析系列(33) : AJAX中的前置过滤器和请求分发器

jQuery1.5以后,AJAX模块提供了三个新的方法用于管理.扩展AJAX请求,分别是: 1.前置过滤器 jQuery. ajaxPrefilter 2.请求分发器 jQuery. ajaxTransport, 3.类型转换器 ajaxConvert 源码结构: jQuery.extend({ /** * 前置过滤器 * @type {[type]} */ ajaxPrefilter: addToPrefiltersOrTransports(prefilters), /** * 请求分发器 *

Jquery源码分析

1.概述 jQuery是一个非常优秀的Js库,与prototype,YUI,Mootools等众多的Js类库相比,它剑走偏锋,从web开发最实用的角度出发,抛除了一些中看但不实用的东西,为开发者提供一个短小精悍的类库.由于其个短小精悍,使用简单方便,性能相对高效.众多的开发者都选择Jquery来进行辅助的web开发. 在使用jquery时开发,我们也会时常碰到许多的问题,但是jquery的代码很晦涩,难起看懂,当开发时出现了问题,看不懂源码,不知道如何去排错. John Resig,Jquery

jquery源码分析(二)——结构

再来复习下整体架构: jQuery源码分析(基于 jQuery 1.11 版本,共计8829行源码) (21,94)                定义了一些变量和函数jQuery=function(){} (96,280)        给jQuery添加一些方法和属性,jQuery.fn=jQuery.prototype(285,347)        extend:        jQuery的一些继承方法        更容易进行后续的扩展                       

jquery 源码学习(四)构造jQuery对象-工具函数

jQuery源码分析-03构造jQuery对象-工具函数,需要的朋友可以参考下. 作者:nuysoft/高云 QQ:47214707 EMail:[email protected] 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 读读写写,不对的地方请告诉我,多多交流共同进步,本章的的PDF等本章写完了发布. jQuery源码分析系列的目录请查看 http://nuysoft.iteye.com/blog/1177451,想系统的好好写写,目前还是从我感兴趣的部分开始,如果大家有对哪

jquery 源码学习(*)

最近在做日志统计程序,发现对方的程序是在Jquery基础上进行开发的,而公司的网站的框架是prototype.而且我也早就想了解一下Jquery源码,故决定研究Jquery源码,模拟它的方法 Jquery这么普及,必有它过人之处,通过开源代码进行学习,是个不错的学习方法啊! 以下是我模拟的方法,我尽量简化方法. 定义对象C(类似于jquery的$方法)——这个也是jquery设计非常巧妙的地方 复制代码 代码如下: (function(){ var _cQuery = window.cQuery

jQuery源码分析系列(38) : 队列操作

Queue队列,如同data数据缓存与Deferred异步模型一样,都是jQuery库的内部实现的基础设施 Queue队列是animate动画依赖的基础设施,整个jQuery中队列仅供给动画使用 Queue队列 队列是一种特殊的线性表,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).队列的特点是先进先出(FIFO-first in first out),即最先插入的元素最先被删除. 为什么要引入队列? 我们知道代码的执行流有异步与同步之分,例如 var a

二.jQuery源码解析之构建jQuery之构建函数jQuery的7种用法

一:$(selectorStr[,限制范围]),接受一个选择器(符合jQuery规范的字符串),返回一个jQuery对象;二:$(htmlStr[,文档对象]),$(html[,json对象])传入html字符串,创建一个新的dom元素 三:$(dom元素),$(dom元素集合)将dom元素转换成jQuery对象.四:$(自定义对象)封装普通对象为jQuery对象.五:$(回调函数)绑定ready事件监听函数,当Dom加载完成时执行.六:$(jQuery对象)接受一个jQuery对象,返回一个j

jquery源码之工具方法

jQuery 作为时下前端的"霸主".它的强大已毋庸置疑.简洁,效率,优雅,易用等优点让人很容易对它珍爱有加. 作为js的小菜,为了提升自我等级,根据各大神博客精辟的解析,硬啃了jQuery源码.在此,并不是要解析啥源码啥的(也没到那个级别哈),读书笔记,仅此而已. 所谓磨刀不误砍柴功,jQuery在大展神通之前也做了许多准备工作.比如说他的一些工具方法: 首当其冲的是他的继承扩展方法: jQuery.extend 其实也不是传统意义的继承,说mixin可能更恰当一些. // 首先看看

jquery源码学习(一)core部分

这一部分是jquery的核心 jquery的构造器 jquery的核心工具函数 构造器 jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQu