深入理解jQuery中的callback

本文要讲的是jQuery.Callback() ,这是一个构造函数。在讲解这个之前,我们需要先讲讲javascript中的回调函数。

什么是回调?

  所谓回调函数,我的理解:回头再调用,即:不是立刻运行,而是在特定时刻触发调用。

js中的同步和异步

在javascript中,回调函数和同步 异步有很深的渊源,所以我们先来理解一下在js中的同步和异步的概念:

  同步: 后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的。

  异步:,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。

更确切的说:回调和异步有很深的渊源,js中的  事件  定时器  ajax  animate等等都用到了回调函数。

为什么jQuery搞了个Callback()?

我们上面说到:js中的  事件  定时器  ajax  animate等等都用到了回调函数,而且回调函数可能有多个。

我们来举个例子:

function a(arg){    console.log(‘吃饭‘ + arg)}function b(arg){    console.log(‘睡觉‘ + arg)}function c(arg){    console.log(‘打豆豆‘ + arg)}

var xiaoming = setTimeout(function(){    a(‘ing‘);    b(‘ing‘);    c(‘ing‘);},1000);console.log(‘------------‘)

  我们在这里用定时器函数实现了异步,在一秒钟后(这句话并不准确,明白为啥不准确的同学们就不要在意这句话了,我是为了方便,不明白的请自行查阅setTimeout函数详解)运行a b c三个函数。

  但是这里有个问题,a b c三个函数和小明耦合是不是有些深? 小明现在每天是吃饭睡觉打豆豆,如果某天他不仅要吃饭睡觉打豆豆,还要学习的话,我们还得修改xiaoming的内部结构。 所以我们改进一下上面的格式:

    function a(arg){
        console.log(‘吃饭‘ + arg)
    }
    function b(arg){
        console.log(‘睡觉‘ + arg)
    }
    function c(arg){
        console.log(‘打豆豆‘ + arg)
    }
    function d(arg){
        console.log(‘学习‘ + arg)
    }

    function callback(){
        var list = [];
        var self = {};
        self.add = function(thing){
            list.push(thing)
        };
        self.fire = function(arg){
            for(var i in list){
                list[i](arg)
            }
        };
        return self
    }

    var xiaomingDo = callback();
    xiaomingDo.add(a);
    xiaomingDo.add(b);
    xiaomingDo.add(c);
    xiaomingDo.add(d);

    var xiaoming = setTimeout(function(){
        xiaomingDo.fire(‘ing‘);     // 弱化耦合
    },1000);
    console.log(‘------------‘)

这样我们就弱化了耦合,将每天具体做的事从xiaoming中分离。

上例中的callback函数建立了一个用来保存函数的数组,最终返回了一个对象,这个对象具有两个方法:add 用来向数组中添加函数 ; fire 用来将数组中的函数全部执行。看到这里是不是感觉很熟悉? 没错,这里采用了观察者模式。

再来个小红:

    var xiaohongDo = callback();
    xiaohongDo.add(a);
    xiaohongDo.add(b);
    var xiaohong = setTimeout(function(){
        xiaohongDo.fire()
    },1000);

是不是感觉逻辑清晰很多。本例中的callback函数与jQuery中的Callback函数的作用以及实现原理是类似的。

jQuery.Callback()大体原理:

  jQuery.Callback()需要在内部维护着一个存储函数的队列数组。

  每当我们调用Callback时,都会返回一个新的对象。

  使用这个对象的方法我们可以对队列数组进行操作。

当然jQuery.Callback()并不是我们想象的那么简单,他还提供了很多可供选择的参数:

  once:保证数组内的函数只执行一遍

  memory:保持以前的fire参数值,每当添加一个新的函数时,会将之前的fire参数值作为参数立即调用新函数

  unique:确保数组中的函数无重复

  stopOnFalse:当一个函数返回false时中断

源码:

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;
};
复制代码

本文参考:http://www.cnblogs.com/aaronjs/p/3342344.html

时间: 2024-10-05 23:06:53

深入理解jQuery中的callback的相关文章

深入理解jQuery中的each方法

写在前面 我们先回顾一下数组中的forEach方法吧.在数组的实例上有个forEach方法供所有实例使用,forEach里面接收一个回调函数,而且回调函数默认接收三个参数:当前项,索引,数组 .forEach循环有个特点,就是无论如何都会把数据遍历完成. each方法 each方法不仅是jQuery的静态方法而且还是jQuery元素原型中的方法 静态方法通过:$.each(数组或对象,函数) 这样的形式执行,注意:数组和对象都可以作为参数哦 原型方法通过:$(匹配元素集合) .each(函数)

深入jQuery中的Callback()

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

深入理解jQuery中的ajax

前面的话 jQuery提供了一些日常开发中需要的快捷操作,例如load.ajax.get和post等,使用jQuery开发ajax将变得极其简单.这样开发人员就可以将程序开发集中在业务和用户体验上,而不需要理会那么繁琐的XMLHTTPRequest对象.jQuery对ajax操作进行了封装,在jQuery中$.ajax()属性最底层的方法,第2层是load().$.get()和$.post()方法,第3层是$.getScript()和$.getJSON()方法.下面将详细介绍jQuery中的aj

深入理解jQuery中$.get、$.post、$.getJSON和$.ajax的用法

当我们用javascript写ajax程序写得很“开心”的时候,突然有人告诉你有一种东西叫jquery,它会告诉你不直接和HttpRequest是多么的快乐,同时你再也不需要再烦恼纠结的ajax乱码问题,更幸福的是你的js代码将大大地简化,看完本文,你会发现,ajax,简单的来讲就是一句话的事情. 本文重点是来讲讲jQuery中调用ajax的4种方法:$.get.$.post.$getJSON.$ajax.如果读者没有javascript和jquery的知识,或者没有ajax的概念,那么请先去问

理解jQuery中$.get、$.post、$.getJSON和$.ajax的用法

ajax的4种方法:$.get.$.post.$getJSON.$ajax. 1.$.get $.get()方法使用GET方式来进行异步请求,它的语法结构为: $.get( url [, data] [, callback] ) url:string类型,ajax请求的地址. data:可选参数,object类型,发送至服务器的key/value数据会作为QueryString附加到请求URL中. callback:可选参数,function类型,当ajax返回成功时自动调用该函数. 例: $.

深入理解jQuery中的Deferred

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

深度理解Jquery 中 offset() 方法

多说无益,现在就开始! 一.语法 1. 返回偏移坐标 $(selector).offset(); top: $(selector).offset().top; left: $(selector).offset().left; 2.设置偏移坐标: $(selector).offset({top:value,left:value}); 参数的含义:{top:value,left:value}     当设置偏移时是必需的.规定以像素为单位的 top 和 left 坐标. 可能的值:(1).名/值对,

深度理解Jquery 中 scrollTop() 方法

这是工作遇到scrollTop() 方法.为了强化自己,把它记录在博客园当中. 下面就开始scrollTop 用法讲解: scrollTop() 定义和用法 scrollTop() 方法设置或返回被选元素的[垂直滚动条位置]. Note: 当滚动条位置位于最顶部时,位置是0:当用于返回位置时:    该方法返回 第一个匹配元素的滚动条的垂直位置.当用于设置位置时:    该方法设置 所有匹配元素的滚动条的垂直位置. scrollTop() 语法 返回滚动条位置 $(selector).scrol

深入理解jQuery、Angular、node中的Promise

最初遇到Promise是在jQuery中,在jQuery1.5版本中引入了Deferred Object,这个异步队列模块用于实现异步任务和回调函数的解耦.为ajax模块.队列模块.ready事件提供基础功能.在用jQuery操作DOM的时候对Promise的使用欲不够强烈,最近学习node和Angular,需要用js写业务逻辑和数据操作代码的时候这种场景需求就出来了.一般来说事件适合在交互场景中运用,因为用户的行为本来就是分散的,而promise这样的流程控制适合在后台逻辑中处理业务. //j