技术标签: ViewUI javascript 设计模式
源码API:http://api.jquery.com/jQuery.Callbacks/
jQuery.Callbacks()是在版本1.7中新加入的。它是一个多用途的回调函数列表对象,提供了一种强大的方法来管理回调函数队列。
那么jQuery.Callbacks使用场景在哪里?
在很多时候需要控制一系列的函数顺序执行。那么一般就需要一个队列函数来处理这个问题
我们看一段代码
function Aaron(List, callback) { setTimeout(function() { var task = List.shift(); task(); //执行函数 if (task.length > 0) { //递归分解 setTimeout(arguments.callee, 1000) } else { callback() } }, 25) } Aaron([function(){ alert('a') },function(){ alert('b') }],function(){ alert('callback') })
分别弹出 ‘a’ , ‘b’ ,’callback’
传入一组函数参数,靠递归解析,分个执行,其实就是靠setTimeout可以把函数加入到队列末尾才执行的原理
*****但是这样写,是不是很麻烦?*****
我们换成jQuery提供的方式
var callbacks = $.Callbacks(); callbacks.add(function() { alert('a'); }) callbacks.add(function() { alert('b'); }) callbacks.fire(); //输出结果: 'a' 'b'
是不是便捷很多了,代码又很清晰,所以它是一个多用途的回调函数列表对象,提供了一种强大的方法来管理回调函数队列。
同时还提供几个便捷的处理参数
once
: 确保这个回调列表只执行( .fire() )一次(像一个递延 Deferred).memory
: 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred).unique
: 确保一次只能添加一个回调(所以在列表中没有重复的回调).stopOnFalse
: 当一个回调返回false 时中断调用var callbacks = $.Callbacks('once'); callbacks.add(function() { alert('a'); }) callbacks.add(function() { alert('b'); }) callbacks.fire(); //输出结果: 'a' 'b' callbacks.fire(); //未执行
once的作用是使callback队列只执行一次
OK,我们大概知道这个是干嘛用的了,可以开始上正菜了。
.Callbacks是在jQuery内部使用,如为.Callbacks是在jQuery内部使用,如为.ajax,$.Deferred等组件提供基础功能的函数,jQuery在1.5引入了Deferred对象(异步列队),jQuery内部基本所有有异步的代码都被promise所转化成同步代码执行了,后期在讨论了
根据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() 从回调列表中的删除一个回调或回调集合。
我们看官网提供的demo
function fn1( value ) { console.log( value ); } function fn2( value ) { fn1("fn2 says: " + value); return false; }
可以将上述两个方法作为回调函数,并添加到 $.Callbacks
列表中,并按下面的顺序调用它们:
var callbacks = $.Callbacks(); callbacks.add( fn1 ); // outputs: foo! callbacks.fire( "foo!" ); callbacks.add( fn2 ); // outputs: bar!, fn2 says: bar! callbacks.fire( "bar!" );
这样做的结果是,当构造复杂的回调函数列表时,将会变更很简单。可以根据需要,很方面的就可以向这些回调函数中传入所需的参数。
上面的例子中,我们使用了 $.Callbacks()
的两个方法: .add()
和 .fire()
。 .add() 可以向回调函数列表中添加新的回调函数,fire() 可以向回调函数中传递参数,并执行回调函数。
设计思想:
先看官网的demo这个列子,涉及到了 add 与 fire方法,熟悉设计模式的童鞋呢,一眼就看出,其实又是基于发布订阅的观察者模式的设计了
pub/sub (观察者模式) 的背后,总的想法是在应用程序中增强松耦合性。并非是在其它对象的方法上的单个对象调用。一个对象作为特定任务或是另一对象的活动的观察者,并且在这个任务或活动发生时,通知观察者。观察者也被叫作订阅者(Subscriber),它指向被观察的对象,既被观察者(Publisher 或 subject)。当事件发生时,被观察者(Publisher)就会通知观察者(subscriber)
作为 $.Callbacks()
的创建组件的一个演示,只使用回调函数列表,就可以实现 Pub/Sub 系统。将 $.Callbacks
作为一个队列
我来模拟下常规最简单的实现
var Observable = { callbacks: [], add: function(fn) { this.callbacks.push(fn); }, fire: function() { this.callbacks.forEach(function(fn) { fn(); }) } }
Observable.add(function() {
alert(1)
})
Observable.fire(function() {
alert(2)
})
Observable.fire(); // 1, 2
构建一个存放回调的数组,如this.callbacks= [] 添加回调时,将回调push进this.callbacks,执行则遍历this.callbacks执行回调。
也弹出1跟2了,实际上jQuery.callbacks是如何处理的呢?
我们看源码
整个$.Callbacks的源码很少,它是一个工厂函数,使用函数调用(非new,它不是一个类)创建对象,它有一个可选参数flags用来设置回调函数的行为。、
对外的接口也就是self的返回
self上的add源码
展开源码
其中有一段代码要单独拿出来
//这里用了一个立即执行的add函数来添加回调 //直接遍历传过来的arguments进行push (function add( args ) { jQuery.each( args, function( _, arg ) { var type = jQuery.type( arg ); //如果所传参数为函数,则push if ( type === "function" ) { if ( !options.unique || !self.has( arg ) ) { //当$.Callbacks('unique')时,保证列表里面不会出现重复的回调 list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) { //假如传过来的参数为数组或array-like,则继续调用添加,从这里可以看出add的传参可以有add(fn),add([fn1,fn2]),add(fn1,fn2) // Inspect recursively add( arg ); } }); })( arguments )
add方法
实参可以是Function, Array
callbacks
类型: Function, Array
一个函数,或者一个函数数组,用来添加到回调列表。
如果是数组会递归调用私有的add函数 list.push( arg );
发现没,设计的原理上其实跟上面发的简单模式 大同小异
fire方法
外观模式 self.fire –> self.fireWith –> fire
最终执行代码是内部私有的fire方法了
fire方法
最终处理的代码
list[ firingIndex ].apply( data[ 0 ], data[ 1 ] )
其实就是拿出list中保存的回调函数,执行罢了,所以整个设计的原理,还是符合我们开始设想的
具体的实现
$.Callbacks( "once" )
确保这个回调列表只执行( .fire() )一次(像一个递延 Deferred).
var callbacks = $.Callbacks( "once" ); callbacks.add( fn1 ); callbacks.fire( "foo" ); //foo 只执行了一次,后面没执行了 callbacks.add( fn2 ); callbacks.fire( "bar" ); callbacks.remove( fn2 ); callbacks.fire( "foobar" );
在fire中调用了 self.disable(); 方法
// 禁用回调列表中的回调。 disable: function() { list = stack = memory = undefined; return this; },
$.Callbacks( "memory" )
保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred).
var callbacks = $.Callbacks("memory"); callbacks.add(function() { console.log("f1"); }); callbacks.fire(); //输出 "f1",这时函数列表已经执行完毕! callbacks.add(function() { console.log("f2"); }); //memory作用在这里,没有fire,一样有结果: f2
在调用 add() 方法时,如果这时 callbacks队列 满足 fired && firing = false(真执行完毕) && memory(需要在构造函数指定),那么add() 进去的回调函数会立即执行,而这个 add 进去的回调函数调用时的参数存储在 memory 变量中。memory 变量用于存储最后一次调用 callbacks.fireWith(...) 时所使用的参数 [context, arguments]。
$.Callbacks( "unique" )
确保一次只能添加一个回调(所以在列表中没有重复的回调)
var f1 = function() { console.log("f1"); }; var callbacks = $.Callbacks(); callbacks.add(f1); callbacks.add(f1); callbacks.fire(); //输出 f1 f1 //传递参数 "unique" callbacks = $.Callbacks("unique"); callbacks.add(f1); //有效 callbacks.add(f1); //添加不进去 callbacks.fire(); //输出: f1
****注意add方法默认不去重,比如这里fn1添加两次,fire时会触发两次****
这里处理很简单
if ( !options.unique || !self.has( arg ) ) { //确保是否可以重复 list.push( arg ); } }
在添加的到处理队列时候,判断一下即可
$.Callbacks( "stopOnFalse" )
:
当一个回调返回false 时中断调用
var f1 = function() { console.log("f1"); return false }; //注意 return false; var f2 = function() { console.log("f2"); }; var callbacks = $.Callbacks(); callbacks.add(f1); callbacks.add(f2); callbacks.fire(); //输出 f1 f2 callbacks = $.Callbacks("memory stopOnFalse"); callbacks.add(f1); callbacks.add(f2); callbacks.fire(); //只输出 f1 callbacks.add(function() { console.log("f3"); }); //不会输出,memory已经失去作用了 callbacks.fire(); //重新触发,输出f1
附源码:
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 < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { // 阻止未来可能由于add所产生的回调 memory = false; // To prevent further calls using add break; //由于参数stopOnFalse为true,所以当有回调函数返回值为false时退出循环 } } //标记回调结束 firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { //从堆栈头部取出,递归fire fire( stack.shift() ); } } else if ( memory ) { //否则,如果有记忆 list = []; } else { //再否则阻止回调列表中的回调 self.disable(); } } }, // Actual Callbacks object // 暴露在外的Callbacks对象,对外接口 self = { // Add a callback or a collection of callbacks to the list add: function() { // 回调列表中添加一个回调或回调的集合。 if ( list ) { // First, we save the current length //首先我们存储当前列表长度 var start = list.length; (function add( args ) { //jQuery.each,对args传进来的列表的每一个对象执行操作 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? // 如果回调列表中的回调正在执行时,其中的一个回调函数执行了Callbacks.add操作 // 上句话可以简称:如果在执行Callbacks.add操作的状态为firing时 // 那么需要更新firingLength值 if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { //如果options.memory为true,则将memory做为参数,应用最近增加的回调函数 firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list // 从函数列表中删除函数(集) remove: function() { if ( list ) { jQuery.each( arguments, function( _, arg ) { var index; // while循环的意义在于借助于强大的jQuery.inArray删除函数列表中相同的函数引用(没有设置unique的情况) // jQuery.inArray将每次返回查找到的元素的index作为自己的第三个参数继续进行查找,直到函数列表的尽头 // splice删除数组元素,修改数组的结构 while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes // 在函数列表处于firing状态时,最主要的就是维护firingLength和firgingIndex这两个值 // 保证fire时函数列表中的函数能够被正确执行(fire中的for循环需要这两个值 if ( 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 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; };
jQuery.Callbacks() 比较简单,也没什么难点
jQuery.Callbacks() 方法的核心是 fire() 方法,将该 fire() 方法作为私有方法被封装在函数中不可直接访问
因此像 memory、firing、fired 这些状态对于外部上下文来说是不可更改的
还有需要注意的是,如果回调函数中使用了 this 对象,可以直接用这个 this 来访问self对象的公有API。当然,也可以用 fireWith() 自己指定 this 的引用对象。
jQuery.Callbacks()的核心思想是 Pub/Sub 模式,建立了程序间的松散耦合和高效通信。
本文转自艾伦 Aaron博客园博客,原文链接:http://www.cnblogs.com/aaronjs/p/3342344.html,如需转载请自行联系原作者
最重要的是要注意参数的顺序一、简介: BeanUtils提供对Java反射和自省API的包装。其主要目的是利用反射机制对JavaBean的属性进行处理。我们知道,一个JavaBean通常包含了大量的属性,很多情况下,对JavaBean的处理导致大量get/set代码堆积,增加了代码长度和阅读代码的难度。二、用法: 如果你有两个具有很多相同属性的JavaBean,一个很常见的情..._beanutil.copy 和put
设置注释模板的入口: Window->Preference->Java->Code Style->Code Template 然后展开Comments节点就是所有需设置注释的元素啦。现就每一个元素逐一介绍:文件(Files)注释标签:/** * @Title: ${file_name} * @Package ${package_name} * @Description:...
SQL语句用于维护管理数据库,包括数据库查询、数据更新、访问控制、对象管理等功能SQL语句分类 DDL 数据定义语言,用于创建数据库对象,如库、表、索引等 DML 数据操作语言,用于对表中的数据进行管理 DQL 数据查询语言,用于从数据表中找到符合条件的数据记录 DCL 数据控制语言,用于设置或更改数据库用户或角色权限 进入数据库_linux写sql语句用写分号嘛
Prim算法的思维路线较为简单,百度基本都能看懂。但是编程还是有点难的。整个围绕两个东西转,一个lowcost数组,一个adjvex数组,前者是用来保存当前生成树到其它顶点的权值的。比如这个图,假设一开始是从0出发,那么首先将0这个点并入到我们的生成树中,顺便设置一遍 lowcost数组,也就是,从0开始到其它顶点的权重。那么,这里有个问题,将0并入到我们的生成树,用什么标记表示这个顶..._普里姆算法c语言实现邻接矩阵中的结构体
kx现今城市化进程飞速发展,错综复杂的路网和越来越复杂的建筑物内部构造,使得人们对于空间位置的认知变得更加困难,尤其是在商场、机场等大型公共场所,室内迷路的情况尤为突出,室内导航因此得到越来越多的关注。室内导航构建人与物之间的空间关系,使用户在室内能精确确定自己的位置,并能快速找到最佳路径到达想要去的位置,为人们的出行带来巨大的便捷。针对人们在室内出行中亟需解决的问题,SuperMap iDesktop 提供了室内导航数据的生产、加载、分析等一体化解决方案,支持构建室内与室外一体的导航模型,实现室内地._supermap 位置 楼层
是中山大学的学者所做的工作。1.要做的事情使用DRL做多标签的图像识别‘2.网络结构states:current region 的特征;reward:分类的正确与否;action:寻找attention local,且在attention区域进行分类;3. 过程首先,将原始图片输入进一个VGG16的卷积层的网络(如下图),得到feature map 。然后经...
题目地址:http://pat.zju.edu.cn/contests/pat-a-practise/1072代码如下:
最近在编写项目的时候发现,将一个文件路径存储到DB中之后,再此运行的时候,沙盒的路径变了,百度之后发现:iOS8,Xcode6之后,由于安全问题,苹果将沙盒路径改成动态路径,每次运行之后,会将以前的沙盒下的东西COPY备份,然后删除以前的沙盒路径,再新建一个路径,将COPY下来的东西啊,放到新的路径下。这也就导致了我的项目存了路径,也找不到文件的问题。那针对以上问题,有以下解决方法:// ...
https://jingyan.baidu.com/article/73c3ce284d8332e50343d917.html
简介CCD和CMOS相机的图像传感器都使用电子快门,两种sensor不同的地方在于读取像素数据的方式。典型的CCD相机会同时曝光所有sensor的像素,这种方式称为全局快门(global shutter)。意思是从相机中sensor获取的图像数据中所有像素的值都在同一个时间点上获取。尽管有部分使用全局快门的CMOS技术存在,但大部分CMOS相机使用卷帘快门(rolling shutter)。使用卷帘快门时,sensor获取的图像以行为单位曝光,每行..._rolling shutter
import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Date;public class DateDemo {public static void main(String[] args) {defindDate();}public static void defi
Linux下Netcat安装在网络工具中有“瑞士军刀”美誉的NetCat(以下简称nc),在我们用了N年了至今仍是爱不释手。因为它短小精悍(这个用在它身上很适合,现在有人已经将其修改成大约10K左右,而且功能不减少)。一个简单而有用的工具,透过使用TCP或UDP协议的网络连接去读写数据。它被设计成一个稳定的后门工具,能够直接由其它程序和脚本轻松驱动。安装方式一:yum安装yum insta..._linux下启动netcat