问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

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的回调函数用法和这个是一样的。都是传一个函数进去,
    在方法内部执行。
    声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
    猫咪丢了之后心里很难过,然后男友担心我就重新去领养了一只小花猫,可是... 有关名侦探柯南黑暗组织!! qq上删除的好友怎么找回来,六个月以上的。只要能找到QQ号就行,我可以... 六个月内删掉的qq好友能找回吗? 急!!因欠费而停机的手机能收到短信吗 手机关机之后停机信息是否可以收到 手机欠费后冲了钱但还是欠费会收到短信吗 手机停机了,包月的流量没用完,能收到微信吗? 德州到潍坊用不用隔离? 南都周刊2011年有一篇社论≪躺在时间的河流上怀念他们≫写得很棒... PHP中的回调函数是怎么实现的? 什么是&quot;回调&quot;在C和它们是如何实现的 PHP中的回调函数是怎么实现的? C++中回调函数的如何实现其逻辑? C++回调函数原理 举个简单的小程序例子 回调函数 钩子函数 有什么区别 php回调函数是什么样子的?靠什么原理运行? c#-回调callback是什么原理,机制? C# C++回调函数原理举个简单的小程序例子 回调函数一般都什么作用求解 回调函数(callback)是什么? ,, 关于回调函数的详细讲解 回调函数是怎么实现的?为什么系统就会去调用回调函数 在拼多多上两个人怎么拼单啊 用呼吸作用的原理分析为什么刚采摘下来的苹果最好吃? 人体吸入氧气呼出二氧化碳,这是基于什么原理? 举例说明呼吸作用原理在日常生活中如何被应用 如何运用呼吸作用的原理使农作物保鲜 呼吸作用释放二氧化碳的原理是 光合作用和呼吸作用的原理是什么? 回调函数实现通知机制这个怎么理解 函数名作为参数传递与回调函数 回调函数的实现 请问使用回调函数,在接收数据的时候就可以不用判... 讲解如何实现C#回调函数 拼多多上面怎么拼单,毕竟喜欢同一件商品的人很少,QQ群上面拼多多互相砍价群,靠谱吗?请知道的说一说 如何为他人挂号? 怎样在我的手机上给别人挂号挂什么挂? 怎么样可以在微信上给家人预约挂号 南京鼓楼医院在手机上绑定家庭成员后怎么帮家人预... 怎样帮别人在我的手机上给别人挂沈阳医大的号 医院的预约号可以帮别人预约挂号吗? 怎样在我的手机上给别人挂沈阳医大的号 华西医院的微信公众号上怎么帮家人挂号 怎么在手机上挂号预约 拼多多商家如何设置拼单人数? 运维工程师以后的发展方向有哪些? IT运维工程师发展前景如何? 做系统运维工程师有前途吗 网络运维就业前景如何?