Callbacks : 对函数的统一管理

Callbacks的options参数接受4个属性,分别是
once : 只执行一次
momery : 记忆
stopOnFalse : 强制退出循环
unique : 唯一

暂时先不管4个属性有什么意思,我们看代码开始部分对options做了处理,如果options是字符串则调用createOptions方法转成json
比如:var cb = $.Callbacks('once momery');
转换为:{once:true,momery:true}
这里就不贴createOptions的源码了,大家可以自己去看一下。

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 );

处理完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 listlist = [],// Stack of fire calls for repeatable listsstack = !options.once && [],// Fire callbacksfire = function( data ) {..................},self = {add: function() {..............},remove: function() {................},has: function( fn ) {..................},empty: function() {..................},disable: function() {..................},disabled: function() {................},lock: function() {..............},locked: function() {..............},fireWith: function( context, args ) {...................},fire: function() {................},fired: function() {................}};

最后Callbacks返回了self对象,所以在self上面定义的仅供Callbacks内部使用。

内部方法中有2个比较重要,一个是list,所有add添加的函数都存放在list中,另一个是fire方法,它实现了触发list中的函数的具体逻辑。稍后我会重点讲一下它。

接下来我们分析一下self方法:

add : 添加函数
remove : 删除函数
has : 检测list中是否有相同的函数
empty : 情空list
disable : 禁止所有操作 list = stack = memory = undefined
disabled : list是否可用
lock : 锁
locked :锁是否可用
fireWith : 为触发list中的函数做预处理,最终调用fire方法
fire : 调用fireWith方法
fired : fire方法是否运行过

通过属性的名称不难发现,disabled、locked、fired这3个方法返回的是状态,代码也比较简单,不多做解释相信大家也能看懂。

disabled: function() {return !list;
},
locked: function() {return !stack;
},
fired: function() {return !!fired;
}

fire方法也比较简单,它调用了fireWith后返回自身,方便链式调用。

fire: function() {self.fireWith( this, arguments );return this;
},

fireWith方法也不算太复杂,它有2个参数:
context : 执行上下文
              我们看fire方法:self.fireWith( this, arguments );
              是不是就很清楚啦,context就是self对象,args就是arguments
args : arguments

然后对args做了处理。把context与args合并在一个数组中。有经验的朋友应该不难看出,这种形式的数组是给apply用的,没错,私有的fire方法中会用到它。

if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {

接着往下看,if ( list && ( !fired || stack ) )  这个判断主要是为了once也就是只fire一次。

fireWith: function( context, args ) {args = args || [];args = [ context, args.slice ? args.slice() : args ];if ( list && ( !fired || stack ) ) {if ( firing ) {stack.push( args );} else {fire( args );}}return this;
},

我对这个判断语句拆分一下,方便理解,list就不说了,主要说后面2个:
首次执行fireWith,因为fired是undefined,在这里取反所以为真,确保fire方法至少执行一次,然后在私有fire方法中赋值为true,下一次再执行到这里取反,则为假。

fire = function( data ) {memory = options.memory && data;fired = true;firingIndex = firingStart || 0;firingStart = 0;......................

看到这里相信大家明白了,要实现once必须stack为假才可以。

stack = !options.once && [],

没有设置options.once,取反为真,则stack为空数组,否则stack等于false

接下去又是一个判断:
if ( firing ) {
          stack.push( args );
} else {
          fire( args );
}

firing是为了避免死循环,当循环内需要执行的函数还没走完,则stack.push( args );

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 addbreak;}}
firing = false;

当循环走完,检测到stack有长度则再调用fire

if ( list ) {if ( stack ) {if ( stack.length ) {fire( stack.shift() );}} else if ( memory ) {list = [];} else {self.disable();}
}

再看add方法,先是一个函数自执行,接收的参数是arguments,然后遍历arguments,判断如果是函数则list.push(arg),否则调用自己add(arg),由此可以看出,add方法不仅可以传一个函数,还能多个函数逗号隔开,如:cb.add(fn1,fn2);

add: function() {if ( list ) {// First, we save the current lengthvar start = list.length;(function add( 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?if ( firing ) {firingLength = list.length;// With memory, if we're not firing then// we should call right away} else if ( memory ) {firingStart = start;fire( memory );}}return this;
},

else if ( memory )  当有记忆功能的时候执行,firingStart = start把循环的起始值设为当前数组的长度值,然后调用fire则只会触发当前添加的函数

私有方法fire定义了索引值、起始值、长度,就开始循环,如果触发的函数返回false,并且options.stopOnFalse为true,则终止循环。

fire = function( 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 ) {memory = false; // To prevent further calls using addbreak;}}firing = false;if ( list ) {if ( stack ) {if ( stack.length ) {fire( stack.shift() );}} else if ( memory ) {list = [];} else {self.disable();}}
},

读了一遍源码后,我们应该对使用Callbacks非常熟悉了:

//once
var cb = $.Callbacks('once');
cb.add(function(){alert('a');
});
cb.add(function(){alert('b');
});
cb.fire();//弹出a,b
cb.fire();//不执行//memory
var cb = $.Callbacks('memory');
cb.add(function(){alert('a');
});
cb.add(function(){alert('b');
});
cb.fire();//弹出a,b

cb.add(function(){ //弹出calert('c');
});//once memory
var cb = $.Callbacks('once memory');
cb.add(function(){alert('a');
});
cb.add(function(){alert('b');
});
cb.fire();//弹出a,b

cb.add(function(){ //弹出calert('c');
});
cb.fire(); //不执行//add方法多个参数逗号隔开
var cb = $.Callbacks();
cb.add(function(){alert('a');
},function(){alert('b');
});cb.fire(); //弹出a,b//stopOnFalse
var cb = $.Callbacks('stopOnFalse');
cb.add(function(){alert('a');return false;
},function(){alert('b');
});cb.fire();//弹出a//lock()
var cb = $.Callbacks('memory');
cb.add(function(){alert('a');
});cb.fire();//弹出a
cb.lock();//锁住fire()

cb.add(function(){ //弹出balert('b');
});
cb.fire();//不执行//remove()
var cb = $.Callbacks();
var fn1 = function(){alert('a');
};
var fn2 = function(){alert('b');
};
cb.add(fn1);
cb.add(fn2);
cb.fire(); //弹出a,b

cb.remove(fn1,fn2);
cb.fire();//不执行

转载于:https://www.cnblogs.com/gongshunkai/p/5927514.html

jquery的回调对象Callbacks详解相关推荐

  1. html标签的下一级遍历,jquery属性,遍历,HTML操作方法详解

    Jquery属性遍历.HTML操作. Jquery拥有可操作HTML元素和属性的强大方法. 下面是我整理的一些jquery遍历函数: .add() 将元素添加到匹配元素的集合中. .andSelf() ...

  2. JavaScript中DOM对象的详解

    *** JavaScript中DOM对象的详解*** DOM对象:Document Object Model,文档对象模型.也称为document(文档对象),是HTML页面当前窗体的内容,是连接JS ...

  3. linux jq 遍历数组,jquery 遍历数组 each 方法详解

    JQuery拿取对象的方式 $('#id') :通过元素的id $('tagName') : 通过元素的标签名 $('tagName tagName') : 通过元素的标签名,eg: $('ul li ...

  4. jquery在html实现遍历,jQuery教程之jQuery遍历、HTML操作详解

    本篇文章探讨了jQuery教程之jQuery遍历.HTML操作详解,希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入. < 什么是遍历? jQuery 遍历,意为"移 ...

  5. jQuery:unbind方法的使用详解

    jQuery:unbind方法的使用详解 一.概述:unbind方法只能解绑用jQuery的bind方法以及用jQuery方法注册的事件处理程序.比如:$('a').click(function(){ ...

  6. openerp经典收藏 对象定义详解(转载)

    对象定义详解 原文地址: http://shine-it.net/index.php/topic,2159.0.html http://blog.sina.com.cn/s/blog_57ded94e ...

  7. python的类和对象_Python面向对象之类和对象实例详解

    本文实例讲述了Python面向对象之类和对象.分享给大家供大家参考,具体如下: 类和对象(1) 对象是什么? 对象=属性(静态)+方法(动态): 属性一般是一个个变量:方法是一个个函数: #类的属性 ...

  8. c语言 is函数,关于C语言回调函数的详解~

    原标题:关于C语言回调函数的详解~ 01 什么是回调函数? 回调函数,光听名字就比普通函数要高大上一些,那到底什么是回调函数呢?恕我读得书少,没有在那本书上看到关于回调函数的定义.我在百度上搜了一下, ...

  9. Scala基础教程--06--类与对象的详解

    Scala基础教程–06–类与对象的详解 章节目标 掌握类和对象的定义 掌握访问修饰符和构造器的用法 掌握main方法的实现形式 掌握伴生对象的使用 掌握定义工具类的案例 1. 类和对象 Scala是 ...

最新文章

  1. rabbitmq-java api
  2. 对接第三方支付接口-类似文件锁的编程小技巧
  3. 开始把一些东西放到博客上
  4. codevs 3981 动态最大子段和
  5. 易语言最大化控件跟随变化_庄子:利己最大化与博弈圈套
  6. 数据转换服务-PDF转换技术
  7. 【计算机网络基础】URI、URN和URL的区别
  8. Survivor空间溢出实例
  9. 倾斜摄影原理与关键技术介绍
  10. 2020年过去了,我很怀念它
  11. phpstorm 免费生成 激活码 保证有效
  12. unity 环境光、模型、材质发绿,绿的发光
  13. 【CodeForces - 1647D】Madoka and the Best School in Russia(分类讨论,因数分解)
  14. 《设计模式之禅》中23种设计模式demo汇总
  15. Linux基础系列(2命令帮助的详细获取)
  16. 3D Human Pose Estimation with Spatial and Temporal Transformers论文笔记
  17. Python爬虫获取“房天下“房价数据(下)
  18. java 循环小数_面试:输出循环小数的循环节
  19. Dell 330显卡驱动在2003server安装问题
  20. ESD 静电抗扰度的预防处理措施

热门文章

  1. 绘画教程:人体结构怎么画?人体躯干怎么画?
  2. 期货开户交易交易风格各异
  3. MySQL中的条件判断函数 CASE WHEN、IF、IFNULL你会用吗?
  4. 关于STM32+OLED显示屏的简单应用
  5. anaconda虚拟环境安装cvxpy报错
  6. 获取微信QQ好友高精地址
  7. 2、Go自动化测试入门-testify
  8. vc++.NET调用oracle10g
  9. 线下实体店结合VR全景,让虚拟购物更加真实
  10. 联想乐云免费5GB不限速手机同步盘随时随地数据随行