jQuery源码之Callbacks的学习

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

jQuery源码学习之Callbacks

jQuery的ajax、deferred通过回调实现异步,其实现核心是Callbacks。

使用方法

使用首先要先新建一个实例对象。创建时可以传入参数flags,表示对回调对象的限制,可选值如下表示。

stopOnFalse:回调函数队列中的函数返回false时停止触发

once:回调函数队列只能被触发一次

memory:记录上一次触发队列传入的值,新添加到队列中的函数使用记录值作为参数,并立即执行。

unique:函数队列中函数都是唯一的

var cb = $.Callbacks('memory');cb.add(function(val){    console.log('1: ' + val)})cb.fire('callback')cb.add(function(val){    console.log('2: ' + val)})// console输出1: callback2: callback

登录后复制

Callbacks 提供了一系列实例方法来操作队列和查看回调对象的状态。

add: 添加函数到回调队列中,可以是函数或者函数数组

remove: 从回调队列中删除指定函数

has: 判断回调队列里是否存在某个函数

empty: 清空回调队列

disable: 禁止添加函数和触发队列,清空回调队列和上一个传入的值

disabled: 判断回调对象是否被禁用

lock: 禁用fire,若memory非空则同时add无效

locked: 判断是否调用了lock

fireWith: 传入context和参数,触发队列

fire: 传入参数触发对象,context是回调对象

源码解析

$.Callback()方法内部定义了多个局部变量和方法,用于记录回调对象的状态和函数队列等,返回self,在self实现了上述回调对象的方法,用户只能通过self提供的方法来更改回调对象。这样的好处是保证除了self之外,没有其他修改回调对象的状态和队列的途径。

其中,firingIndex为当前触发函数在队列中的索引,list是回调函数队列,memory记录上次触发的参数,当回调对象实例化时传入memory时会用到,queue保存各个callback执行时的context和传入的参数。self.fire(args)实际是self.fireWith(this,args),self.fireWith内部则调用了在Callbacks定义的局部函数fire。

    ...    // 以下变量和函数 外部无法修改,只能通过self暴露的方法去修改和访问    var // Flag to know if list is currently firing        firing,        // Last fire value for non-forgettable lists        // 保存上一次触发callback的参数,调用add之后并用该参数触发        memory,        // Flag to know if list was already fired        fired,        // Flag to prevent firing        // locked==true fire无效 若memory非空则同时add无效        locked,        // Actual callback list        // callback函数数组        list = [],        // Queue of execution data for repeatable lists        // 保存各个callback执行时的context和传入的参数        queue = [],        // Index of currently firing callback (modified by add/remove as needed)        // 当前正触发callback的索引        firingIndex = -1,        // Fire callbacks        fire = function() {            ...        },                // Actual Callbacks object        self = {            // Add a callback or a collection of callbacks to the list            add: function() {                ...            },            ...            // Call all callbacks with the given context and arguments            fireWith: function( context, args ) {                if ( !locked ) {                    args = args || [];                    args = [ context, args.slice ? args.slice() : args ]; // :前为args是数组,:后是string                    queue.push( args );                    if ( !firing ) {                        fire();                    }                }                return this;            },            // Call all the callbacks with the given arguments            fire: function() {                self.fireWith( this, arguments );                return this;            },            ...        }

登录后复制

通过self.add添加函数到回调队列中,代码如下。先判断是否memory且非正在触发,如果是则将fireIndex移动至回调队列的末尾,并保存memory。接着使用立即执行函数表达式实现add函数,在该函数内遍历传入的参数,进行类型判断后决定是否添加到队列中,如果回调对象有unique标志,则还要判断该函数在队列中是否已存在。如果回调对象有memory标志,添加完毕之后还会触发fire,执行新添加的函数。

            add: function() {                if ( list ) {                    // If we have memory from a past run, we should fire after adding                    // 如果memory非空且非正在触发,在queue中保存memory的值,说明add后要执行fire                    // 将firingIndex移至list末尾 下一次fire从新add进来的函数开始                    if ( memory && !firing ) {                        firingIndex = list.length - 1;                        queue.push( memory );                    }                    ( function add( args ) {                        jQuery.each( args, function( _, arg ) {                            // 传参方式为add(fn)或add(fn1,fn2)                            if ( jQuery.isFunction( arg ) ) {                                /**                                 * options.unique==false                                 * 或                                 * options.unique==true&&self中没有arg                                 */                                if ( !options.unique || !self.has( arg ) ) {                                    list.push( arg );                                }                            } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {                                // 传参方式为add([fn...]) 递归                                // Inspect recursively                                add( arg );                            }                        } );                    } )( arguments ); //arguments为参数数组 所以add的第一步是each遍历                    //添加到list后若memory真则fire,此时firingIndex为回调队列的最后一个函数                    if ( memory && !firing ) {                        fire();                    }                }                return this;            }

登录后复制

fire、fireWith方法内部实际调用了局部函数fire,其代码如下。触发时,需要更新fired和firing,表示已触发和正在触发。通过for循环执行队里中的函数。结束循环后,将firingIndex更新为-1,表示下次触发从队列中的第一个函数开始。遍历在fireWith中更新过的queue,queue是保存数组的数组,每个数组的第一个元素是context,第二个元素是参数数组。执行函数时要是否返回false且回调对象有stopOnFalse标志,如果是则停止触发。

// Fire callbacks        fire = function() {            // Enforce single-firing            // 执行单次触发            locked = locked || options.once;            // Execute callbacks for all pending executions,            // respecting firingIndex overrides and runtime changes            // 标记已触发和正在触发            fired = firing = true;            // 循环调用list中的回调函数            // 循环结束之后 firingIndex赋-1 下一次fire从list的第一个开始 除非firingIndex被修改过            // 若设置了memory,add的时候会修改firingIndex并调用fire            // queue在fireWith函数内被更新,保存了触发函数的context和参数            for ( ; queue.length; firingIndex = -1 ) {                memory = queue.shift();                while ( ++firingIndex < list.length ) {                     // Run callback and check for early termination                    // memory[0]是content memory[1]是参数                    if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&                        options.stopOnFalse ) {                                                // Jump to end and forget the data so .add doesn't re-fire                        // 当前执行函数范围false且options.stopOnFalse==true 直接跳至list尾 终止循环                        firingIndex = list.length;                        memory = false;                    }                }            }            // 没设置memory时不保留参数            // 设置了memory时 参数仍保留在其中            // Forget the data if we're done with it            if ( !options.memory ) {                memory = false;            }            firing = false;            // Clean up if we're done firing for good            if ( locked ) {                // Keep an empty list if we have data for future add calls                if ( memory ) {                    list = [];                // Otherwise, this object is spent                } else {                    list = "";                }            }        },

登录后复制

  

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

相关推荐:

js 异步for循环的介绍

以上就是jQuery源码之Callbacks的学习的详细内容,更多请关注【创想鸟】其它相关文章!

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

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

(0)
上一篇 2025年3月8日 04:13:30
下一篇 2025年3月3日 12:43:29

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

相关推荐

  • 关于react项目静态类型检查方案

    这篇文章主要介绍了关于react项目静态类型检查方案,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 为什么需要引入类型检查 JS作为一个弱类型语言,具有很大的灵活性,但是它的优点也是它的缺点,它很容易让我们忽视一些隐晦的逻辑…

    2025年3月8日
    200
  • ES6 Promise中then与catch的返回值的实例

    这篇文章主要介绍了关于es6 promise中then与catch的返回值的实例,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 一.catch为then的语法糖 故then方法与catch方法均会返回一个Promise对象(…

    2025年3月8日 编程技术
    200
  • Vue脚手架的简单使用

    这篇文章主要介绍了关于vue脚手架的简单使用,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 提前申明 注意:自己对vue脚手架的理解和认识,很多东西和理解都是形象上的手法,并不专业和官方 webpack 一种项目构建工具,可…

    2025年3月8日
    200
  • Vue iview-admin框架二级菜单改为三级菜单的方法

    这篇文章主要介绍了关于vue iview-admin框架二级菜单改为三级菜单的方法,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 最近在用 iview-admin的Vue后台模板,从git上下载后发现左侧导航栏最多支持到二级…

    2025年3月8日 编程技术
    200
  • 通过Vue属性$route的params传参

    这篇文章主要介绍了关于通过vue属性$route的params传参,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 vue传值 与 vue传参是两块东西 概念图 原理 vue传参的原理主要在于 Vue.$route.param…

    2025年3月8日
    200
  • vscode搭建Typescript+React+Dva的开发环境

    这篇文章主要介绍了关于vscode搭建typescript+react+dva的开发环境,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 [ 作为2018年前端最应该学的技术 ], Typescript 确实惹火, 这两天崩崩…

    2025年3月8日 编程技术
    200
  • JS字符串转数字的方法

    这篇文章主要介绍了关于js字符串转数字的方法,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 String转换为Number有很多种方式,我可以想到的有5种!但总有一些情况让人猝不及防,总结到这里供自己日后查找方便 parse…

    编程技术 2025年3月8日
    200
  • addEventListener中的EventListener的接口对象

    这篇文章主要介绍了关于addeventlistener中的eventlistener接口对象,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 写在最前 遗漏的知识点:addEventListener的第二个参数不光可以传入一个…

    编程技术 2025年3月8日
    200
  • jQuery中css()和attr()方法的区别

    这篇文章主要介绍了关于jquery中css()和attr()方法的区别,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 方法介绍: css(): html代码: 登录后复制登录后复制 jQuery代码: 立即学习“前端免费学习…

    编程技术 2025年3月8日
    200
  • jquery源码的基本介绍

    这篇文章主要介绍了关于jquery源码学习一之概况,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 jQuery jQuery是继prototype之后又一个优秀的Javascript框架。它是轻量级的js库 ,它兼容CSS3…

    2025年3月8日
    200

发表回复

登录后才能评论