首页

文章

jquery中的回调函数怎么用

发布网友 发布时间:2022-04-19 20:08

我来回答

3个回答

懂视网 时间:2022-05-14 22:25

对于$.Callbacks 创建的Callback对象,它的addfire方法就是,其实就是基于发布订阅(Publish/Subscribe)的观察者模式的设计。

// 模拟一下这种模式
function aa() {
 console.log('aa');
}
function bb() {
 console.log('bb');
}
var m_db = {
 Callbacks: [],
 add: function(fn) {
 this.Callbacks.push(fn);
 },
 fire: function() {
 this.Callbacks.forEach(function(fn){
  fn();
 })
 }
}
m_db.add(aa);
m_db.add(bb);
m_db.fire();
  • 设计原理

  • 开始构建一个存放回调的数组,如this.callbacks= [] 添加回调时,将回调push进this.callbacks,执行则遍历this.callbacks执行回调,也弹出1跟2了。当然这只是简洁的设计,便于理解,整体来说设计的思路代码都是挺简单的,那么我们从简单的设计深度挖掘下这种模式的优势。


    模式的实际使用
    // 首先看一个场景
    $.ajax({
     url: '',
     ..
    }).done(function(data) {
     // 第一步处理数据
     
     // 第二步处理DOM
     $('aaron1').html(data.a)
     $('aaron2').html(data.b)
     $('aaron3').html(data.c) 
     
     // 其余处理
     
    })

    首先,所有的逻辑都写在done方法里面,这样确实是无可厚非的,但是问题就是逻辑太复杂了。Done里面有数据处理html渲染、还可能有其它不同场景的业务逻辑。这样如果是换做不同的人去维护代码,增加功能就会显得很混乱而且没有扩展性。

    $.ajax({
     url: '',
     ..
    }).done(function(data) {
     // 第一步处理数据
     processData(data);
     // 第二步处理DOM
     processDom(data);
     
     // 其余处理
     processOther(data);
    })

    这样看着时好一些了,通过同步执行来一次实现三个方面的处理,每一方面的处理都提取出来,但是这样的写法几乎就是“就事论事”的处理,达不到抽象复用。

    var m_cb = {
     callbacks: [],
     add: function(fn){
     this.callbacks.push(fn);
     },
     fire: function(data){
     this.callbacks.forEach(function(fn){
      fn(data);
     })
     }
    }
    m_cb.add(function(data){
     // 数据处理
    })
    m_cb.add(function(data){
     // DOM处理
     
    })
    
    m_cd.add(function(data){
     // 其余处理
    })
    $.ajax({
     url: '',
     ...
    }).done(function(data){
     m_cd.fire(data);
    })

    这样使用了观察者模式之后是不是感觉好多了呢,设计该模式背后的主要动力是促进形成松散耦合。在这种模式中,并不是一个对象调用另一个对象的方法,而是一个对象订阅另一个对象的特定活动并在状态改变后获得通知。订阅者也称为观察者,而被观察的对象称为发布者或主题。当发生了一个重要的事件时,发布者将会通知(调用)所有订阅者并且可能经常以事件对象的形式传递消息。

    总之、观察者模式就是将函数/业务处理管理起来,当一定的事件触发或者时某一任务执行完毕后,一次性执行。


    三、$.Callbacks()

    对于$.Callbacks 创建的Callback对象,它的addfire方法就是,其实就是基于发布订阅(Publish/Subscribe)的观察者模式的设计。

    $.Callbacks一般的开发者使用的较少,它的开发实现主要时为$.ajax以及$.deferred

    jQuery.Callbacksjquery在1.7版本之后加入的,是从1.6版中的_Deferred对象中抽离的,主要用来进行函数队列的add、remove、fire、lock等操作,并提供once、memory、unique、stopOnFalse四个option进行一些特殊的控制。

    这个函数常使用的就是在事件触发机制中,也就是观察者设计模式的订阅和发布模式中,$.Callbacks主要出现在ajax、deferred、queue中。

  • 下面来仔细分析一下该方法的使用吧

  • 1、先来跑一下流程
    function aa() {
     console.log('aa');
    }
    function bb() {
     console.log('bb');
    }
    
    var cb = $.Callbacks();
    cb.add(aa);
    cb.add(bb);
    cb.fire(); 
    // aa
    // bb
    function fn1(value) {
     console.log(value);
    }
    
    function fn2(value) {
     fn1("fn2 says: " + value);
     return false;
    }
    
    var cb1 = $.Callbacks();
    cb1.add(fn1); // 添加一个进入队列
    cb1.fire('foo'); // 执行一下
    // foo
    cb1.add(fn2); // 再添一个
    cb1.fire('bar'); // 一次性执行
    // bar
    // fn2 says: bar
    cb1.remove(fn2); // 移除一个
    cb1.fire('111'); // 执行剩下的那一个
    // 111

    $.Callbacks()就是一个工厂函数。

  • jQuery.Callbacks() 的 API 列表如下:

  • callbacks.add() :回调列表中添加一个回调或回调的集合。
    callbacks.disable() :禁用回调列表中的回调。
    callbacks.disabled() :确定回调列表是否已被禁用。 
    callbacks.empty() :从列表中删除所有的回调。
    callbacks.fire() :用给定的参数调用所有的回调。
    callbacks.fired() :访问给定的上下文和参数列表中的所有回调。 
    callbacks.fireWith() :访问给定的上下文和参数列表中的所有回调。
    callbacks.has() :确定列表中是否提供一个回调。
    callbacks.lock() :锁定当前状态的回调列表。
    callbacks.locked() :确定回调列表是否已被锁定。
    callbacks.remove() :从回调列表中的删除一个回调或回调集合。
  • 源码结构

  • jQuery.Callbacks = function(options) {
     // 首先对参数进行缓冲
     options = typeof options === "string" ?
     (optionsCache[options] || createOptions(options)) :
     jQuery.extend({}, options);
     // 实现代码
     // 函数队列的处理
     fire = function() {}
     
     // 自身方法
     self = {
     add: function() {},
     remove: function() {},
     has: function(fn) {},
     empty: function() {},
     disable: function() {},
     disabled: function() {},
     lock: function() {},
     locked: function() {},
     fireWith: function(context, args) {},
     fire: function() {},
     fired: function() {}
     };
     
     
     return self;
    };
  • 参数处理

  • // 处理通过空格分隔的字符串
    var str = "once queue";
    var option = {};
    $.each(str.match(/S+/g) || [], function (_index, item) {
     option[item] = true;
    })
    console.log(option);
    // {once: true, queue: true}
    Callbacks内部维护着一个List数组。这个数组用于存放我们订阅的对象,它是通过闭包来实现长期驻存的。添加回调时,将回调push进list,执行则遍历list执行回调。

    Callbacks 有4个参数。


    1. once 的作用是使callback队列只执行一次。

    var callbacks = $.Callbacks('once');
    
    callbacks.add(function() {
     alert('a');
    })
    
    callbacks.add(function() {
     alert('b');
    })
    
    callbacks.fire(); //
    输出结果: 'a' 'b' callbacks.fire(); //未执行
    // 来看一下具体怎么实现
    // jQuery是在执行第一个fire的时候直接给清空list列表了,然后在add的地方给判断下list是否存在,从而达到这样的处理
    function Callbacks(options){
     var list = [];
     var self = {};
     self: {
     add: function(fn){
      list.push(fn);
     },
     fire: function(data){
      this.list.forEach(function(item){
      item(data);
      })
      if(options == 'once') {
      list = undefined;
      }
     }
     
     }
     return self;
    }
    // $jQuery.Callbacks的处理,在fire中调用了 self.disable(); 方法
    // 禁用回调列表中的回调。
    disable: function() {
     list = stack = memory = undefined;
     return this;
    }
  • memory 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调

  • function fn1(val) {
     console.log('fn1 says ' + val);
    }
    function fn2(val) {
     console.log('fn2 says ' + val);
    }
    function fn3(val) {
     console.log('fn3 says ' + val);
    }
    
    var cbs = $.Callbacks('memory');
    cbs.add(fn1);
    cbs.fire('foo'); // fn1 says foo
    
    console.log('..........')
    
    cbs.add(fn2); // 这里在添加一个函数进入队列的同时,就立马执行了这个 回调了
    cbs.fire('bar'); 
    // fn2 says foo 这个东东比较特殊~
    // fn1 says bar
    // fn2 says bar
    
    console.log('..........')
    cbs.add(fn3);
    cbs.fire('aaron');
    // fn3 says bar
    // fn1 says aaron 
    // fn2 says aaron
    // fn3 says aaron
    // 需要解决的问题一个就是如何获取上一个参数,以及add后的执行
    function Callbacks(options) {
     var list = [];
     var self;
     var firingStart;
     var memory;
    
     function _fire(data) {
     memory = options === 'memory' && data;
     firingIndex = firingStart || 0; // 
     firingStart = 0;
     firingLength = list.length;
     for (; list && firingIndex < firingLength; firingIndex++) {
     list[firingIndex](data)
     }
     }
    
     self = {
     add: function(fn) {
     var start = list.length;
     list.push(fn)
     // 如果参数是memory
     if (memory) {
     firingStart = start; //获取最后一值
     _fire(memory); // 同时执行
     }
     },
     fire: function(args) {
     if (list) {
     _fire(args)
     }
     }
     }
     return self;
    }
  • Unique:确保一次只能添加一个回调(所以在列表中没有重复的回调)

  • function fn1(val) {
     console.log('fn1 says ' + val);
    }
    var callbacks = $.Callbacks( "unique" );
    callbacks.add( fn1 );
    callbacks.add( fn1 ); // repeat addition
    callbacks.add( fn1 );
    callbacks.fire( "foo" );
  • stopOnFalse: 当一个回调返回false 时中断调用

  • function fn1(value) {
     console.log(value);
     return false;
    }
    
    function fn2(value) {
     fn1("fn2 says: " + value);
     return false;
    }
    
    var callbacks = $.Callbacks("stopOnFalse");
    callbacks.add(fn1);
    callbacks.fire("foo");
    
    callbacks.add(fn2);
    callbacks.fire("bar");
    
    // foo
    // bar
    $.callback()的源码
    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;
    };
    未完待续~~

    热心网友 时间:2022-05-14 19:33

           因为javascript的运行机制的原因,jquery作为成熟的工具,为javascript提供了大量的回调函数(callback)。

           因为回调函数的工作原理以及工作时机,很多新手对回调函数不能熟练运用。回调函数的重要作用之一,就是在某项操作完成时,进行下一项操作。这两项操作功能上不一定耦合,但是逻辑上却必须耦合。意思是前一个操作实现的功能(效果)可以和回调函数实现的功能毫无联系,但是二者在代码逻辑上却是耦合的。耦合的意思是说,当一操作进行后,二操作会紧跟进行,二者的运行关系密不可分。回调函数增加了程序的耦合性,使代码的逻辑更容易理解。

           回调函数的用处在javascript中可谓处处体现,比如最为重要的就是AJAX回调。

           Jquery中提供了大量大回调函数,比如complete,success,fail等操作。这些操作的调用时机就如同他们的名字一样。success以为着成功的时候调用,等。

          举个简单的例子,以jquery中的动画为例,现在我想实现一个让id为animate的dom对象宽度边长的动画,在这个对象变长之后,我想要提示用户,这个对象已经变长了。因此,我得使用以下的代码:

    $("#animate").animate({
        width: 200
    }, {
        ration: 500,
        success: function() {
            alert("width 已经变长了");
        },
        error: function() {
            alert("出现了错误");
        }
    });

    以上的代码定义了两个回调函数success和error,顾名思义,success是在成功的时候就会调用,error是在出错误之后调用。

    这就是回调函数的基本运用。

    看到这儿,或许你会问,为什么我不直接用这样的代码呢?

    try {
        $("#animate").animate({
            width: 2000
        }, 500, "linear");
        alert(""width已经变长了);
    } catch(error) {
        alert("出错了");
    }

    如果你测试你会发现,动画并未完成,就出现了alert弹窗。

    这就是javascript的运行机制所造成的,javascript的运行机制分为同步和异步,异步操作会破坏脚本的执行流,使得程序跳过异步(异步仍然在进行)而进入接下来的程序,异步操作即使在进行,也不会影响javascript的继续执行,也就是不会产生阻塞。同步操作中的定时操作有着与异步很相似的运行机制,但是依然是同步操作。以上的错误正是由于定时操作引起的。

    回调函数的作用,最为重要的运用,就是运用在异步操作和定时操作。

    jquery中运用回调函数最重要的地方有两个,一个是动画回调(就是在动画执行完成,或者执行错误,或者执行失败的时候调用),以及ajax(同动画)。ajax与回调是密不可分的。

    热心网友 时间:2022-05-14 20:51

    举个js里面最简单的例子,
    数组的sort,
    Array.sort(function(a,b){return a-b})
    sort里面的函数就是回调函数。
    jquery的回调函数用法和这个是一样的。都是传一个函数进去,
    在方法内部执行。
    历史要怎么读,有啥诀窍 高中历史诀窍 年终会活动策划方案 深度解析:第一财经回放,探索财经新风向 逆水寒手游庄园怎么邀请好友同住 逆水寒手游 逆水寒不同区可以一起组队吗? 逆水寒手游 逆水寒怎么进入好友世界? 逆水寒手游 逆水寒怎么去别人的庄园? 使用puppeteer实现将htmll转成pdf 内卷时代下的前端技术-使用JavaScript在浏览器中生成PDF文档 【译】将HTML转为PDF的几种实现方案 变形金刚08动画怎么样 变形金刚08动画的问题 变形金刚08动画日语版剧情介绍 高分!换显卡nvidia控制面板被我卸了,重新安装显卡驱动后没了nvidia控... 我的nvidia控制面板被卸载了 怎么找回啊 卸载后 这个画面看着很奇怪_百 ... 李卓彬工作简历 林少明工作简历 广东工业职业技术学院怎么样 郑德涛任职简历 唐新桂个人简历 土地入股的定义 ups快递客服电话24小时 贷款记录在征信保留几年? 安徽徽商城有限公司公司简介 安徽省徽商集团新能源股份有限公司基本情况 安徽省徽商集团有限公司经营理念 2019哈尔滨煤气费怎么有税? 快手删除的作品如何恢复 体育理念体育理念 有关体育的格言和理念 什么是体育理念 万里挑一算彩礼还是见面礼 绿萝扦插多少天后发芽 绿萝扦插多久发芽 扦插绿萝多久发芽 炖牛排骨的做法和配料 网络诈骗定罪标准揭秘 “流水不争先”是什么意思? mc中钻石装备怎么做 为什么我的MC里的钻石块是这样的?我想要那种。是不是版本的问题?如果是... 带“偷儿”的诗句 “君不见巴丘古城如培塿”的出处是哪里 带“奈何”的诗句大全(229句) 里翁行()拼音版、注音及读音 带“不虑”的诗句 “鲁肃当年万人守”的出处是哪里 无尘防尘棚 进出口报关流程,越详细越好。谢谢大家指教。 双线桥不是看化合价升多少就标多少的吗?为什么CL2+2KI=2KCL+I2中I失... PHP中的回调函数是怎么实现的? 什么是"回调"在C和它们是如何实现的 PHP中的回调函数是怎么实现的? C++中回调函数的如何实现其逻辑? C++回调函数原理 举个简单的小程序例子 回调函数 钩子函数 有什么区别 php回调函数是什么样子的?靠什么原理运行? c#-回调callback是什么原理,机制? C# C++回调函数原理举个简单的小程序例子 回调函数一般都什么作用求解 回调函数(callback)是什么? ,, 关于回调函数的详细讲解 回调函数是怎么实现的?为什么系统就会去调用回调函数 在拼多多上两个人怎么拼单啊 用呼吸作用的原理分析为什么刚采摘下来的苹果最好吃? 人体吸入氧气呼出二氧化碳,这是基于什么原理? 举例说明呼吸作用原理在日常生活中如何被应用 如何运用呼吸作用的原理使农作物保鲜 呼吸作用释放二氧化碳的原理是 光合作用和呼吸作用的原理是什么? 回调函数实现通知机制这个怎么理解 函数名作为参数传递与回调函数 回调函数的实现 请问使用回调函数,在接收数据的时候就可以不用判... 讲解如何实现C#回调函数 拼多多上面怎么拼单,毕竟喜欢同一件商品的人很少,QQ群上面拼多多互相砍价群,靠谱吗?请知道的说一说 如何为他人挂号? 怎样在我的手机上给别人挂号挂什么挂? 怎么样可以在微信上给家人预约挂号 南京鼓楼医院在手机上绑定家庭成员后怎么帮家人预... 怎样帮别人在我的手机上给别人挂沈阳医大的号 医院的预约号可以帮别人预约挂号吗? 怎样在我的手机上给别人挂沈阳医大的号 华西医院的微信公众号上怎么帮家人挂号 怎么在手机上挂号预约 拼多多商家如何设置拼单人数? 运维工程师以后的发展方向有哪些? IT运维工程师发展前景如何? 做系统运维工程师有前途吗 网络运维就业前景如何?
    声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com