jquery中的callbacks之我见

callbacks是jquery的核心之一。

语法如下:

jQuery.Callbacks( flags )   flags 类型: String 一个用空格标记分隔的标志可选列表,用来改变回调列表中的行为。

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

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

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

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

使用案例:

function fn1(val) {
        console.log(‘fn1 says ‘ + val);
    }
    function fn2(val) {
        console.log(‘fn2 says ‘ + val);
        return false;
    }
    var cbs = $.Callbacks(‘once memory‘);
    cbs.add(fn1);
    //第一次fire会缓存传入的参数
    cbs.fire(‘foo‘); //fn1 says foo
    //fire过一次之后,以后的add都会自动调用fire,传入的参数是上次fire传入的‘foo‘
    cbs.add(fn2); //fn2 says foo
 function fn1(val) {
        console.log(‘fn1 says ‘ + val);
    }
    function fn2(val) {
        console.log(‘fn2 says ‘ + val);
        return false;
    }
    var cbs = $.Callbacks(‘stopOnFalse‘);
    cbs.add(fn2)
    cbs.add(fn1);
    cbs.fire(‘foo‘); //fn2 says foo
  
function fn1(val) {
        console.log(‘fn1 says ‘ + val);
    }
    function fn2(val) {
        console.log(‘fn2 says ‘ + val);
        return false;
    }
    var cbs = $.Callbacks(‘once‘);
    cbs.add(fn2)
    cbs.add(fn1);
    cbs.fire(‘foo‘); //fn2 says foo    fn1 says foo

    cbs.add(fn2);
    cbs.fire("bar"); //不输出

源码分析:

var optionsCache = {};

    // Convert String-formatted options into Object-formatted ones and store in cache
    //20170618 huanhua 参数options="once memory"
    // 返回结果: optionsCache[once memory]={once:true,memory:true}
function createOptions( options ) {
    var object = optionsCache[options] = {};
    //20170618 huanhua 前面已经定义正则 core_rnotwhite= /\S+/g , options.match( core_rnotwhite )返回数组["once","memory"]类似这种
    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 // Flag to know if list is currently firing
        firing,
        // Last fire value (for non-forgettable lists)
        //20170618 huanhua 存储的是最后一次 fire传递的参数值
        memory,
        // Flag to know if list was already fired
        fired,
        // End of the loop when firing
        firingLength,
        // Index of currently firing callback (modified by remove if needed)
        firingIndex,
        // First callback to fire (used internally by add and fireWith)
        firingStart,
        // Actual callback list
        // 20170618 huanhua 回调函数的存储列表
        list = [],
        // Stack of fire calls for repeatable lists
        // 20170618 huanhua 回调函数不是只执行一次的时候,用 stack保存正在执行回调的时候,再次请求执行的 fire()中传入的参数
        stack = !options.once && [],
        // Fire callbacks
        fire = function (data) {//20170618 data是一个数组[context,arg],第一个是执行的上下午,第二个是真正的参数
            memory = options.memory && data;
            fired = true;
            firingIndex = firingStart || 0;
            firingStart = 0;
            firingLength = list.length;
            firing = true;
            for (; list && firingIndex < firingLength; firingIndex++) {
                //20170618 huanhua 判断
                if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
                    // 阻止未来可能由于add所产生的回调,add中有这段代码
                    //else if ( memory ) {
                   // firingStart = start; //20170618 huanhua 从列表最新添加的回调函数位置开始执行
                   // fire( memory ); //20170618 huanhua 立即执行回调函数
                  //}
                    memory = false; // To prevent further calls using add
                    break;//由于参数stopOnFalse为true,所以当有回调函数返回值为false时退出循环
                }
            }
            firing = false;
            if (list) {
                //20170618 huanhua 处理正在执行回调中,再次触发的 fire(),也就是同时第一个触发的 fire还没执行完,紧接着多次执行了 fire
                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) {
                        //20170618 huanhua add(fun1,fun2,fun3)
                        jQuery.each( args, function( _, arg ) {
                            var type = jQuery.type(arg);
                            //20170618 huanhua 判断是函数
                            if (type === "function") {
                                //20170618 huanhua  !options.unique判断 当$.Callbacks(‘unique‘)时,保证列表里面不会出现重复的回调
                                //!self.has( arg )只要不列表里不存在,就添加到列表
                                if ( !options.unique || !self.has( arg ) ) {
                                    list.push( arg );
                                }
                                //20170618 huanhua 判断传递的参数是类似数组的对象,或者就是数组
                                // add({leng:2,0:fun1,1:fun2},{leng:1,0:fun3});
                            } else if ( arg && arg.length && type !== "string" ) {
                                // Inspect recursively
                                add( arg );
                            }
                        });
                    })( arguments );
                    // Do we need to add the callbacks to the
                    // current firing batch?
                    //20170618 huanhua 当fire执行的时间需要比较长的时候,我们在执行 add的时候,fire也在执行,add执行完后,fire还没执行完,为了防止新加的不执行,
                    //所以重新赋值了需要执行的回调函数个数 firingLength = list.length;
                    if ( firing ) {
                        firingLength = list.length;
                    // With memory, if we‘re not firing then
                    // we should call right away
                    //20170618 huanhuaa $.CallBacks("memory"),并且还调用了 fire
                    } else if ( memory ) {
                        firingStart = start; //20170618 huanhua 从列表最新添加的回调函数位置开始执行
                        fire( memory ); //20170618 huanhua 立即执行回调函数
                    }
                }
                return this;
            },
            // Remove a callback from the list
            //20170618 huanhua 删除指定的回调函数
            remove: function () {
                //20170618  huanhua 判断回调函数列表是有效的
                if ( list ) {
                    jQuery.each( arguments, function( _, arg ) {
                        var index;
                        //20170618 huanhua index = jQuery.inArray( arg, list, index )获取需要被删除的回调函数在列表中的位置
                        while ((index = jQuery.inArray(arg, list, index)) > -1) {
                            //20170618 huanhua 删除回调函数
                            list.splice( index, 1 );
                            // Handle firing indexes
                            //20170618 huanhua 如果删除的时候在执行 fire()
                            if (firing) {
                                //20170618 huanhua 当被删除的回调函数的位置 <= 回调函数列表总长度的时候,删除了一个回调函数,所以 firingLength 要减一个了
                                if ( index <= firingLength ) {
                                    firingLength--;
                                }
                                //20170618 huanhua 当被删除的回调函数的位置 <= 执行到的回调函数的位置时候,删除了一个回调函数,所以 firingIndex 要减一个了
                                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) {//回调函数是否在列表中.
                //20170616 huanhua !!( list && list.length ) 如果执行的 has("")/has(undefined)等等,如果 执行了  def.disable(),list就等于undefined了
                return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
            },
            // Remove all callbacks from the list
            //20170618 huanhua 清空回调函数列表
            empty: function() {
                list = [];
                return this;
            },
            // Have the list do nothing anymore
            //20170618 huanhua 禁用回调列表中的回调。
            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 ) {
                args = args || [];
                args = [ context, args.slice ? args.slice() : args ];
                if (list && (!fired || stack)) {
                    //20170618 huanhua 如果正在执行回调函数
                    //将参数推入堆栈,等待当前回调结束再调用
                    if ( firing ) {
                        stack.push(args);
                    //20170618 huanhua 如果当前不是正在执行回调函数,就直接执行
                    } 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;
};
时间: 2024-10-12 03:54:35

jquery中的callbacks之我见的相关文章

jQuery中的$.Callbacks回调

前言 在js中使用回调函数是很常见的.但是当触发一个事件时需要同时执行多个回调函数时,我们可能就需要一个队列来把这些回调函数存放起来,当事件触发时按照队列的先进先出原则依次调用这些回调函数.在jQuery中就是使用$.Callbacks这个工具方法来管理回掉函数队列的. 1 function fn1(){ 2 console.log(1); 3 } 4 function fn2(){ 5 console.log(2); 6 } 7 var callbacks = $.Callbacks(); 8

jQuery中的$.Deferred、$.when异步操作

前言 网页中常常会出现一些耗时比较长的操作,如ajax请求服务器数据,这些操作都不能立即得到结果.如果我们需要在这些操作执行完后来进行另外的操作,我们就需要将这些操作放在回调函数中,$.Deferred就是jQuery用来处理回调操作的.jQuery中的$.Deferred对$.Callbacks很有依赖,看看$.Callbacks执行回调. 1 var callbacks = $.Callbacks(); 2 setTimeout(function(){ 3 console.log(1); /

深入理解jQuery中的Deferred

引入 1  在开发的过程中,我们经常遇到某些耗时很长的javascript操作,并且伴随着大量的异步. 2  比如我们有一个ajax的操作,这个ajax从发出请求到接收响应需要5秒,在这5秒内我们可以运行其他代码段,当响应到达后,我们需要判断响应的结果(无非就是成功或者失败),并根据不同的结果  添加回调函数. 3  为了有效的简洁的添加回调函数jQuery引入了Callbacks. 4  而为了方便的 根据不同的结果(或者根据各种跟结果有关的逻辑,比如不管是成功或者失败) 添加回调函数,jQu

jQuery源码分析-jQuery中的循环技巧

Js代码   作者:nuysoft/JS攻城师/高云 QQ:47214707 EMail:[email protected] 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 前记:本文收集了jQuery中出现的各种遍历技巧和场景 Js代码   // 简单的for-in(事件) for ( type in events ) { } Js代码   // 缓存length属性,避免每次都去查找length属性,稍微提升遍历速度 // 但是如果遍历HTMLCollection时,性能提升非常

深入jQuery中的Callback()

引入 初看Callback函数很不起眼,但仔细一瞅,发现Callback函数是构建jQuery大厦的无比重要的一个基石.jQuery中几乎所有有关异步的操作都会用到Callback函数. 为什么搞了个Callback函数? 1  在 js 开发中,经常会遇到同步和异步这两个概念. 2  在javascript中神马是同步?神马是异步? 听我讲一个相亲的故事(本故事并不准确,仅供参考): 1 藤篮是一个漂亮姑娘,明年就要30岁了可现在还没有对象,于是,她的母亲给她报名了两家相亲机构,一家名叫同步相

详解jQuery中 .bind() vs .live() vs .delegate() vs .on() 的区别

转载自:http://zhuzhichao.com/2013/12/differences-between-jquery-bind-vs-live/ 我见过很多开发者很困惑关于jQuery中的.bind(), .live(), .delegate() 和 .on() 的使用以及它们的不同. 如果你没有耐心或者只想看到总结的话,请直接跳转到结尾. 在我们深入了解这些方法之前,先准备好HTML代码. [javascript] view plain copy <pre> <ul id=&quo

jQuery中的deferred实现机制

jquery中的源码: Deferred: function( func ) { var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail",

深入理解jQuery中的callback

本文要讲的是jQuery.Callback() ,这是一个构造函数.在讲解这个之前,我们需要先讲讲javascript中的回调函数. 什么是回调? 所谓回调函数,我的理解:回头再调用,即:不是立刻运行,而是在特定时刻触发调用. js中的同步和异步 在javascript中,回调函数和同步 异步有很深的渊源,所以我们先来理解一下在js中的同步和异步的概念: 同步: 后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的.同步的. 异步:,每一个任务有一个或多个回调函数(c

网站开发div在Jquery中的鼠标事件失去焦点

网站div在Jquery中的鼠标事件失去焦点,今天在做网站开发的时候遇到了个问题,就是我在用Jquery做导航的时候,用到了Jquery的鼠标事件mouseout,但问题出来了,我有两个div,A的div包含了B的div,本来我是想当鼠标移出A的div的时候隐藏div,可是当我的鼠标移到B的div在移出的时候也会触发jquery的隐藏事件,这让人很头痛. 在网上搜了下,找到了解决的法子,就是直接把mouseout换成了mouseleave就可以了,具体的问题请大家参考Jquery的开发文档吧