这篇文章主要介绍了关于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