jQuery源码学习之Callbacks

jQuery的ajaxdeferred通过回调实现异步,其实现核心是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: callback
2: 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 firingfiring,// Last fire value for non-forgettable lists// 保存上一次触发callback的参数,调用add之后并用该参数触发memory,// Flag to know if list was already firedfired,// 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 callbacksfire = function() {...},// Actual Callbacks objectself = {// Add a callback or a collection of callbacks to the listadd: function() {...},...// Call all callbacks with the given context and argumentsfireWith: function( context, args ) {if ( !locked ) {args = args || [];args = [ context, args.slice ? args.slice() : args ]; // :前为args是数组,:后是stringqueue.push( args );if ( !firing ) {fire();}}return this;},// Call all the callbacks with the given argumentsfire: 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 recursivelyadd( arg );}} );} )( arguments ); //arguments为参数数组 所以add的第一步是each遍历//添加到list后若memory真则fire,此时firingIndex为回调队列的最后一个函数if ( memory && !firing ) {fire();}}return this;}

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

// Fire callbacksfire = 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 itif ( !options.memory ) {memory = false;}firing = false;// Clean up if we're done firing for goodif ( locked ) {// Keep an empty list if we have data for future add callsif ( memory ) {list = [];// Otherwise, this object is spent} else {list = "";}}},

jQuery源码学习之Callbacks相关推荐

  1. jquery源码学习笔记三:jQuery工厂剖析

    jquery源码学习笔记二:jQuery工厂 jquery源码学习笔记一:总体结构 上两篇说过,query的核心是一个jQuery工厂.其代码如下 function( window, noGlobal ...

  2. jQuery源码学习(1)——addClass

    最近比较闲,寻思着学习下jQuery源码,看了好多博客,很多都讲的比较详细.jQuery虽然只有那么200多K,但内容却比较丰富,对于我这样一个js菜鸟,看起来相当吃力.骨头太大,只能化整为零,从简单 ...

  3. jquery源码学习笔记一:总体结构

    练武不练功,到老一场空.计算机也一样. 计算机的功,就是原理.如果程序员只会使用各种函数,各种框架,而不知其原理,顶多熟练工人而已.知其然,更要知其所以然. jquery我们用得很爽,但它究竟咋实现的 ...

  4. jQuery源码学习

    $.jQuery是什么? 平时天天在用的$到底是个什么东西?$("id")思考,感觉像个工厂方法.提供selector创建jquery对象. 一看源码绕晕了,剥茧抽丝吧 定义jqu ...

  5. jQuery源码学习第二天--jQuery的extend扩展

    Jquery中的extend扩展 一.看下常见的extend扩展: 1: jQuery.extend({ 2: noConflict: function( deep ) { 3: if ( windo ...

  6. jQuery源码学习(6)-Sizzle选择器(2)

    1.CSS选择器的位置关系: 四种关系:"+" 紧挨着的兄弟关系:">" 父子关系:" " 祖先关系:"~" 之后 ...

  7. jQuery源码学习(5)-Sizzle选择器(1)

    1.常见的选择器: #test表示id为test的DOM节点 .aaron 表示class为aaron的DOM节点 nav>li 表示在nav内部子li的样式,而不是所有的后代元素,只是往下一层 ...

  8. 通过jQuery源码学习javascript(三)

    序 承接上两篇继续写下去.我尽量把我明白的地方给大家说清楚.有些大家的提问我也有点搞不明白,如果有人能解答,再好不过了. 疑问 第一篇中有位博友提出了以下的问题,我也不太明白,如果有明白的,能否告知一 ...

  9. jQuery源码学习视频

    为什么80%的码农都做不了架构师?>>>    http://bbs.miaov.com/forum.php?mod=viewthread&tid=7385&extr ...

最新文章

  1. Spring踩坑记录
  2. 数据蒋堂 | 做基础软件要投入很多钱?
  3. Spring实战3-Spring之旅
  4. 地址空间和虚拟内存(转载)http://topic.csdn.net/u/20090619/10/4c62a13b-536b-4b0a-af09-2271c6a104e1.html...
  5. 初步了解Linux创建文件系统命令
  6. c++ 利用内存映射读取大文件
  7. MVVM是什么?谈谈你对MVVM的理解?
  8. a:hover span 隐藏/显示 问题
  9. ngnix学习(二)ngnix常用命令
  10. jquery 的模块化
  11. win10解决IE浏览器安装不上的问题
  12. 云网融合赋能智慧转型,“天翼云管 ”开启贴身云管家时代
  13. 实时Linux之PREEMPT_RT篇
  14. 用c语言实现文本文件中的字符筛选分析(二)
  15. ANSI、C99、C11 标准区别详解
  16. 语音识别(ASR)论文优选:SynthASR: Unlocking Synthetic Data for Speech Recognition
  17. RTL8211F 硬件配置
  18. win11安装使用安卓子系统WSA
  19. 如何提高信息流广告的转化率?
  20. 二、CRUD操作以及配置解析

热门文章

  1. 北大校友马里千:计算机视觉商用的下一个十年,AI 生成应占有一席之地
  2. NeurIPS 2019 少样本学习研究亮点全解析
  3. SAP MM 自定义条件类型出现在采购信息记录的'条件'界面里 ?
  4. SAP MM ME57界面看到的供应源跟Source List主数据不一致?
  5. 浅析丨AI安防产品发展现状与趋势分析
  6. SAP MM 公司间STO里交货单PGI之后自动触发内向交货单功能的实现
  7. 科大讯飞董事长:AI创业,做平台已没有机会
  8. 心得丨走过最长的路,就是机器学习过程中的弯路
  9. chinese_L-12_H-768_A-12的一个坑
  10. 量化因果涌现表明:宏观可以战胜微观