jQuery源码之回调函数的解析

这篇文章主要介绍了关于jquery源码之回调函数的解析,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下

回调函数

一、概念

回调函数是一个通过函数指针来调用执行的函数,如果你把一个函数的指针作为参数传递出去,那么这个指针调用这个函数的时候,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

好处: 使用回调函数进行处理,代码就可以继续进行其他任务,而无需空等。实际开发中,经常在javascript中使用异步调用。

异步回调

$(document).ready(callback);$(document).on(‘click’,callback)$.ajax({  url: "aaron.html",  context: document}).done(function() {         //成功执行}).fail(function() {        //失败执行);$('#clickme').click(function() {    $('#book').animate({        opacity: 0.25,        left: '+=50',        height: 'toggle'    }, 5000, function() {        // Animation complete.    });});

登录后复制

同步

var test1 = function(callback) {    //执行长时间操作    callback();}test1(function() {    //执行回调中的方法});一个同步(阻塞)中使用回调的例子,目的是在test1代码执行完成后执行回调callback

登录后复制

所以理解回调函数最重要的2点:

1、一个回调函数作为参数传递给另一个函数是,我们仅仅传递了函数定义。我们并没有在参数中执行函数。我们并不传递像我们平时执行函数一样带有一对执行小括号()的函数

2、回调函数并不会马上被执行,它会在包含它的函数内的某个特定时间点被“回调”。

二、观察者模式

在理解jquery的回调对象之前我们先来学习一下观察者模式(SB模式):

观察者模式: 一个对象作为一个特定任务的观察者,当这个任务出发或者执行完毕之后通知观察者(Subscriber)。观察者也可以叫做订阅者,它指向被观察者(Publisher),当事件发生时, 被观察者会通知观察者。

对于$.Callbacks 创建的Callback对象,它的add和fire方法就是,其实就是基于发布订阅(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对象,它的add和fire方法就是,其实就是基于发布订阅(Publish/Subscribe)的观察者模式的设计。

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

jQuery.Callbacks是jquery在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'); // 执行一下// foocb1.add(fn2); // 再添一个cb1.fire('bar'); // 一次性执行// bar// fn2 says: barcb1.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个参数。

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 fooconsole.log('..........')cbs.add(fn2);  // 这里在添加一个函数进入队列的同时,就立马执行了这个 回调了cbs.fire('bar'); // fn2 says foo 这个东东比较特殊~// fn1 says bar// fn2 says barconsole.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 additioncallbacks.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  -1 ) {                            list.splice( index, 1 );                            // Handle firing indexes                            // 在函数列表处于firing状态时,最主要的就是维护firingLength和firgingIndex这两个值                            // 保证fire时函数列表中的函数能够被正确执行(fire中的for循环需要这两个值                            if ( firing ) {                                if ( index <= firingLength ) {                                    firingLength--;                                }                                if ( index  -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;};

登录后复制

未完待续~~

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

以上就是jQuery源码之回调函数的解析的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2746684.html

(0)
上一篇 2025年3月8日 04:11:31
下一篇 2025年3月8日 04:11:43

AD推荐 黄金广告位招租... 更多推荐

相关推荐

发表回复

登录后才能评论