jquery源码分析(四)——回调对象 Callbacks
借用百度百科来说明下回调函数:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
jQuery回调对象实现恰好利用了设计模式中的观察者模式思想,观察者模式 (pub/sub) 的背后,总的想法是在应用程序中增强松耦合性。并非是在其它对象的方法上的单个对象调用。一个对象作为特定任务或是另一对象的活动的观察者,并且在这个任务或活动发生时,通知观察者。观察者也被叫作订阅者(Subscriber),它指向被观察的对象,既被观察者(Publisher 或 subject)。当事件发生时,被观察者(Publisher)就会通知观察者(subscriber)
具体到开发中,我们会在事件触发,定时器,ajax、动画(transformend)使用回调函数。
jQuery中回调函数队列管理模块:Callbacks从1.6版中的_Deferred对象中抽离出来的。其设计原理是开始构建一个存放回调的数组,再通过add、remove、fire、lock等操作来控制函数队列的管理,并提供once、memory、unique、stopOnFalse四个option进行一些特殊的控制。
代码如下:
var rnotwhite = (/\S+/g);//匹配空格,返回数组// String to Object options format cache var optionsCache = {};// Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) {var object = optionsCache[ options ] = {};jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {//接受的字符串的组合传参数,可以使用空格分割object[ flag ] = true;});return object; }/** Create a callback list using the following parameters:* By default a callback list will act like an event callback list and can be* "fired" multiple times.** Possible options://这里提供四个可选参数once、memory、unique、stopOnFalse 对函数队列进行特殊控制。*/ jQuery.Callbacks = function( options ) {// Convert options from String-formatted to Object-formatted if needed// (we check in cache first)options = typeof options === "string" ?( optionsCache[ options ] || createOptions( options ) ) :jQuery.extend( {}, options );var // Flag to know if list is currently firing firing,// Last fire value (for non-forgettable lists) memory,// Flag to know if list was already fired fired,// End of the loop when firing firingLength,// Index of currently firing callback (modified by remove if needed) firingIndex,// First callback to fire (used internally by add and fireWith) firingStart,// Actual callback listlist = [],// Stack of fire calls for repeatable listsstack = !options.once && [],//传递once参数后,则回调执行完毕之后,list会被清空([])或置为undefined// Fire callbacksfire = function( data ) {//闭包方法memory = options.memory && data;fired = true;//用来判断回调队列是否被执行过一次firingIndex = firingStart || 0;//上次fire的位置,用作设置memory时,add之后直接触发回调。firingStart = 0;firingLength = list.length;firing = true;for ( ; list && firingIndex < firingLength; firingIndex++ ) {if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//判断是否需要判读回调返回值memory = false; // To prevent further calls using add---//强制修改memorybreak;}}firing = false;if ( list ) {if ( stack ) {//是否为false,初始化时是通过是否传递once来控制,if ( stack.length ) {fire( stack.shift() );}} else if ( memory ) {//这里清空目的在于,add之后直接fire新添加的回调。list = [];} else {self.disable();}}},// Actual Callbacks objectself = {// Add a callback or a collection of callbacks to the listadd: function() {if ( list ) {// First, we save the current lengthvar start = list.length;(function add( args ) {//添加回调list集合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?if ( firing ) {firingLength = list.length;// With memory, if we're not firing then// we should call right away} else if ( memory ) {//这里如果option.memory有值的话,memory已经被修改为[context,arguments]firingStart = start;fire( memory );}}return this;},// Remove a callback from the listremove: function() {//从回调队列中 删除函数。if ( list ) {jQuery.each( arguments, function( _, arg ) {var index;while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {list.splice( index, 1 );// Handle firing indexesif ( 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 listempty: function() {//清空回调队列list = [];firingLength = 0;return this;},// Have the list do nothing anymoredisable: function() {//整个回调队列失效list = stack = memory = undefined;return this;},// Is it disabled?disabled: function() {return !list;},// Lock the list in its current statelock: function() {//在当前状态下锁死stack = undefined;if ( !memory ) {self.disable();}return this;},// Is it locked?locked: function() {return !stack;},// Call all callbacks with the given context and argumentsfireWith: function( context, args ) {if ( list && ( !fired || stack ) ) {args = args || [];args = [ context, args.slice ? args.slice() : args ];if ( firing ) {stack.push( args );} else {fire( args );//若$.Callback调用时设置memory,则会闭包中memory被修改为此时的args<==>[context,args]}}return this;},// Call all the callbacks with the given argumentsfire: function() {//切换执行上下问self.fireWith( this, arguments );return this;},// To know if the callbacks have already been called at least oncefired: function() {//判断回调队列已经被触发过一次return !!fired;}};return self; };
1、功能上看:callbacks可以用来在队列中添加回调,执行回调,删除回调等等。并提供一些参数如once,memory,unique等来进行特殊需求的控制。
2、回调模块结构组织:首先构建一个存放回调的队列,如var list = [],通过闭包使回调队列所占内存空间不被释放添加回调时,将回调push进list,fire时遍历list将回调队列中函数执行一遍。
jQuery.Callbacks()的API列表如下
callbacks.add() 往回调队列中添加一个回调或回调的集合。 callbacks.disable() 禁用回调列表中的回调 callbacks.disabled() 判断回调列表是否已被禁用。 callbacks.empty() 从列表中删除所有的回调. callbacks.fire() 用给定的参数执行所有的回调 callbacks.fired() 判断是否回调队列是否被至少fire一次。 callbacks.fireWith() 访问给定的上下文和参数列表中的执行所有回调。 callbacks.has() 确定列表中是否包含一个回调 callbacks.lock() 锁定当前状态的回调列表。 callbacks.locked() 确定回调列表是否已被锁定。 callbacks.remove() 从回调列表中的删除一个回调或回调集合。
代码定义了多个局部变量,来看看他们用处:
list:保存回掉函数队列 通过闭包使回调数组所占内存空间不被释放,添加回调时,将回调push进list,执行时则遍历list执行回调
firing:判断是否正在执行回调队列。
firingStart:记住当前fire的位置,当参数为memory时使用到。
stack:如果当前正在执行回调(或者说回掉队列未执行完),即firing为true,将要fire的回调保存到栈中,若未设置了once为true ,待回调队列执行完成之后就会执行该回掉。若设置了once 则确保这个回调列表只执行( .fire() )一次。
memory :若设置了memory,保持以前的值,后面添加到这个回调队列会立即执行该回调(像一个递延 Deferred)
firingStart:用于add 回调之后作为立即执行该回调的索引(在设置memory之后使用)
重点提下fire方法: self.fire –> self.fireWith –> fire 最终执行代码是内部私有的fire方法,内部几个逻辑处理了以下几种情况
(1)、每fire 一次都遍历list回调队列,直到结束或者有一个回调函数返回false (只有设置了stopOnFalse控制逻辑才起作用),才中止执行回调
(2)、接下来处理了,回调队列正在执行时(firing为true),add 了一个回调,并不会立即处理而是push 到stack之后。 待回调队列执行完成后再根据stack判断是否fire新增的回调。
disable方法:list = stack = memory = undefined; 后续所有操作都失效
3、参数的使用方式:参数用 一个用空格标记分隔的标志可选列表,用来改变回调列表中的行为:通过以下这些参数进行特殊需求的控制
once
: 确保这个回调列表只执行( .fire() )一次.memory
: 保持以前的值,当执行add之后,将运行添加到这个列表的后面的最新的回调,参数保存在memory中(像一个递延 Deferred).unique
: 确保一次只能添加一个回调(所以在列表中没有重复的回调).stopOnFalse
: 当一个回调返回false 时中断调用
当新建一个Callbacks回调队列时,可以通过这些参数定义回调队列fire 之后的行为。
默认情况下,回调列表(不传递参数情况下)可以被多次触发(fire),下面来看看参数具体如何使用及其在源码中如何发挥作用的。
(1)、var callbacks = $.Callbacks( "once" ); —— fire一次之后list被清空
(2)、
var callbacks = $.Callbacks( "memory" );—— memory的实现思路就是回调队列list在fire之后(不管是否设置once),
以后add的时候自动调用fire,将新添加的回调执行一遍。(类似递延)
(3)、
var callbacks = $.Callbacks( "
unique
" );——控制回调函数的唯一性。通过has方法判断是否存在队列中。
(4)、
var callbacks = $.Callbacks( "stopOnFalse" );——控制是否需要回调函数返回值是否为false来终止回调队列的执行。
(5)、
var callbacks = $.Callbacks( "once memory" );—— 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调,once的时候只允许add一次,在触发fire之后就会理清掉list。
转载于:https://www.cnblogs.com/hoboStage/p/4948659.html
jquery源码分析(四)——回调对象 Callbacks相关推荐
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- jQuery源码分析-each函数
本文部分截取自且行且思 jQuery.each方法用于遍历一个数组或对象,并对当前遍历的元素进行处理,在jQuery使用的频率非常大,下面就这个函数做了详细讲解: 复制代码代码 /*! * jQuer ...
- jQuery 源码分析第一篇之入口源码
目前阅读的是jQuery 1.11.3的源码,有参考nuysoft的资料.原来比较喜欢在自己的Evernote上做学习基类,并没有在网上写技术博客的习惯,现在开始学习JS的开源代码,想跟大家多交流,希 ...
- Spring 源码分析(四) ——MVC(二)概述
随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) from:Spring 源码分析(四) --MVC(二)概述 - 水门-kay的个人页面 - OSCHINA ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
- [转] jQuery源码分析-如何做jQuery源码分析
jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习. 当我读到难度的书或者源码时,会和<如何阅读一本书> ...
- java中Mark接口_JVM源码分析之Java对象头实现
原标题:JVM源码分析之Java对象头实现 原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 "365篇原创计划"第十一篇. 今天呢!灯塔君跟大家讲: JVM源码分析之Ja ...
- ABP源码分析四十七:ABP中的异常处理
ABP源码分析四十七:ABP中的异常处理 参考文章: (1)ABP源码分析四十七:ABP中的异常处理 (2)https://www.cnblogs.com/1zhk/p/5538983.html (3 ...
最新文章
- Java反射实践:从反射中理解class
- 网站推广专员浅析不做大幅修改如何调整网站推广内容?
- 安装Exchange2003时出0XC1037AE6错误的解决方法.
- STM32开发 -- Keil使用(2)
- ngx_lua模块中的共享内存字典项API
- 开发工具:收集12 个顶级 Bug 跟踪工具,值得收藏!
- python 默认配置文件_python各类配置文件写法
- 数分项目《泰坦尼克》——Task1
- html照片苹果手机,iPhone如何拍出漂亮唯美的照片
- ubuntu格式化磁盘并挂载
- 怎么简单的生成SSL证书
- php配置站点报错403,phpstudy V8 报403错误怎么办
- 大数据分析工程师大纲
- 【turtle】画一朵漂亮的玫瑰花,真的很漂亮
- lenovo thinkpad t460s opensuse linux 下禁用多点触控屏
- python爬虫如何防止IP屏蔽
- H5移动端css实现向右横向滚动功能
- 谷歌生物医学专用翻译_[Windows]学生党福利,文献翻译神器,附有生物医学专用引擎哦!...
- Unity 3D 入门基础
- 用直流电机测转速——测速发电机标定实验
热门文章
- Eclipse利用正则表达式快速去掉注释的方法
- Weblogic内存调整
- [设计模式-结构型]装饰模式(Decorator)
- android h5链接蓝牙,h5+runtime Native.js 混合APP连接蓝牙打印机
- Tomcat安装后没有出现tomcat主页
- IDEA导入Gradle项目后,重现构建项目并导入jar包后但是External Libraries目录中无任何引入的jar包
- node python 后台启动_NodeJS后台
- 手桌面上没有计算机,手把手教你电脑桌面图标都不见了怎么办
- svd奇异值分解_Lecture 28 | 奇异值分解
- Oracle收集用户的权限