jquery的2.0.3版本源码系列(6):2880-3042行,回调对象,对函数的统一管理

时间:2022-09-01 15:27:57

目录

1 . 回调对象callbacks的演示

回调的使用有一点像事件绑定,先绑定好,等到有点击事件或者其他时就触发。

 <script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
alert(1);
}
function bbb(){
alert(2);
}
function ccc(){
alert(3);
}
var cb=$.Callbacks();
cb.add(aaa);
cb.add(bbb);
cb.add(ccc);
cb.fire();
//看起来有点像事件绑定,add负责添加,fire负责触发
</script>

工作原理相当于add方法负责收集事件list,fire负责统一触发,触发时以for循环来做到。

jquery的2.0.3版本源码系列(6):2880-3042行,回调对象,对函数的统一管理

回调对象的好处是统一管理。

看一个未使用回调对象的例子:

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
alert(1);
}
(function(){
function bbb(){
alert(2);
}
})();
aaa();//可调用
bbb();//报错,说not defined
</script>

那么为了能够调用匿名函数里的bbb函数,可以绑定到全局的回调对象上。

<script src="js/jquery-2.0.3.js"></script>
<script>
var cb=$.Callbacks();
function aaa(){
alert(1);
}
cb.add(aaa);
(function(){
function bbb(){
alert(2);
};
cb.add(bbb);
})();
cb.fire();//依次弹出1和2
</script>

2.callbacks的参数说明

1.4个选项

回调函数有once、memory、unique、stopOnFalse个选项。

once的作用是只能触发一次。

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
alert(1);
}
function bbb(){
alert(2);
}
var cb=$.Callbacks('once');
cb.add(aaa);
cb.add(bbb);
cb.fire();//生效
cb.fire();//并不生效
</script>

memory的作用是记忆功能。

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
alert(1);
}
function bbb(){
alert(2);
}
var cb=$.Callbacks('memory');
cb.add(aaa);
cb.fire();//两个函数都生效
cb.add(bbb);
</script>

unique的作用是对相同的函数去重。

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
alert(1);
}
function bbb(){
alert(2);
}
var cb=$.Callbacks('unique');
cb.add(aaa);
cb.add(aaa);
cb.fire()//这样就只触发一次aaa
</script>

stopOnFalse的作用是当函数返回值为false时就不再执行后续函数。

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(){
alert(1);
return false;
}
function bbb(){
alert(2);
}
var cb=$.Callbacks('stopOnFalse');
cb.add(aaa);
cb.add(bbb);
cb.fire()//遇到返回False,那么后续就不再执行
</script>

另:callbacks接收多个选项的组合,比如 var cb=$.Callbacks('once unique'); 。

2.options源码

有3个部分

var optionsCache = {};

这里定义了选项缓存的空对象。

function createOptions( options ) {
var object = optionsCache[ options ] = {};
//定义了一个options作为属性的对象
jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true;
});
//options.match( core_rnotwhite )根据空格分割字符串,并返回结果数组
return object;
}

那么传进来的选项options进行字符串分割, var cb=$.Callbacks('once unique');  的option匹配的结果是 ['one','unique'] 。接下来在each循环里第一个选项时索引,第二个选项分别是one和unique。

所以把选项存进optionsCache并返回object。

<script>
var optionsCache={};
function cache (options){
var object = optionsCache[ options ] = {};
object[options]=true;
console.log(object);//{once:true}
}
cache('once');
</script>

创建选项object

options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) :
jQuery.extend( {}, options );

这段代码判断了options是不是字符串,如果不是,比如 $.Callbacks() 那么返回的就是空对象。如果是字符串,创建固定格式的选项,能从缓存里面取就直接取,不能就构造出来。类似如下:

optionsCache:{
'once memory':{once:true,memory:true}
}
options:{once:true,memory:true}

3.1  定义了一些变量

1.memory

2.fired

3.firing

4.firingStart

5.firingLength

6.firingIndex

7.list是实际的回调列表

这是回调对象的最重要的一个数组了,通过把回调函数push到list中,以此来决定触发的列表。

8.stack

stack = !options.once && [] 通过代码可以看到如果选项的once为true,那么结果为false,过选项的once为false也就是不要设置,那么stack就为true了。

9.fire方法

它是一个辅助方法,供self对象的fire、fireWith等调用。

10.self对象

定义了对外的方法。一旦使用jQuery.callbacks就会返回self对象,那么其间定义的方法就可以被调用了。

3.2  add方法

add方法放在self对象里。

1.分析无参的情况。为了测试这个方法做了什么。这里介绍一个执行源代码的小技巧。先把代码设置为

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa() {
alert(1);
}
function bbb(){
alert(2);
}
var cb=$.Callbacks();
</script>

还未使用add方法

var start = list.length;
console.log(arguments);

源代码添加控制台log方法

这里的打印是代码初始化的过程。

jquery的2.0.3版本源码系列(6):2880-3042行,回调对象,对函数的统一管理

准备工作好了。

脚本代码如下:

function aaa() {
alert(1);
}
function bbb(){
alert(2);
}
var cb=$.Callbacks();
cb.add(aaa);
cb.add(bbb);

这个时候的console.log打印,那么内置arguments对象就分别是aaa和bbb了。

jquery的2.0.3版本源码系列(6):2880-3042行,回调对象,对函数的统一管理

为什么会each遍历呢,针对的是 cb.add(aaa,bbb); ,那么argument对象就有了2个元素了。

jquery的2.0.3版本源码系列(6):2880-3042行,回调对象,对函数的统一管理

self = {
// 添加一个回调或者一个回调列表
add: function() {
if ( list ) {
//第一次进入的时候当然list空数组,start等于0,第二次等于1.
var 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" ) {
// 这里处理的是add([aaa,bbb])数组类型的参数
add( arg );
}
});
})( arguments ); .........
}
return this;
},

通过一个自执行add匿名函数,对arguments进行遍历。

如果为function进入if分支。这个时候有一个unique判断。当然无参的时候unique是undefined咯,所以会进入list.push方法。那么也就是说所有添加的函数都会被push到list中。

如果出现数组参数,比如 cb.add([aaa,bbb]); ,那么就把这个数组再递归调用一遍,当然是遍历数组咯。

2.处理unique参数

if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}

我们看到unique一开始为true,那么 !options.unique 第一次进入时为false,就要看self是否有arg了。第一次添加某个函数,false取反就为true。第二次添加相同的函数,true取反就为false了。所以第二次添加相同的函数时,是不可能push到list中的。那么也就实现了unique的唯一添加目标了。

3.处理memory参数

我们知道memory参数的作用是如果回调已经被触发了,那么再次添加add方法,会自动触发。

 <script src="js/jquery-2.0.3.js"></script>
<script>
function aaa() {
alert(1);
}
function bbb(){
alert(2);
}
var cb=$.Callbacks("memory");
cb.add(aaa);
cb.fire();
cb.add(bbb);//弹出2 </script>

那么在add方法里,memory是什么呢,搜索一下,2909行memory等于之前触发的data memory = options.memory && data; 。

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

比如,

<script src="js/jquery-2.0.3.js"></script>
<script>
function aaa(n) {
alert(n);
}
function bbb(){
alert(2);
}
var cb=$.Callbacks("memory");
cb.add(aaa);
cb.fire(999);//fire函数传参
cb.add(bbb);//弹出2 </script>

这个时候memory就被赋值为如下打印内容,自然是包含传进去的参数999的。同时因为add方法里的start赋值,现在已经变为了1,通过firingStart的矫正,那么就只触发list最后一个函数了。

jquery的2.0.3版本源码系列(6):2880-3042行,回调对象,对函数的统一管理

3.3   remove方法

// 从触发list里移除某个回调
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
// 先遍历触发列表,然后通过splice方法裁减掉
while( ( index = jQuery.inArray( arg, list, index ) ) > - ) {
list.splice( index, );
// Handle firing indexes
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},

首先上脚本代码。一旦函数aaa被remove掉,就不会被触发。

 <script src="js/jquery-2.0.3.js"></script>
<script>
function aaa() {
alert(1);
}
function bbb(){
alert(2);
}
var cb=$.Callbacks();
cb.add(aaa,bbb);
cb.remove(bbb);
cb.fire();//bbb并不会被触发
</script>

其原理是从list中删除掉。

根据源码,先找出arguments,其打印结果为

jquery的2.0.3版本源码系列(6):2880-3042行,回调对象,对函数的统一管理

那么接下来就看源码执行了。

通过each方法可以把要remove掉的函数取出来。 ( index = jQuery.inArray( arg, list, index ) ) > -1 是把remove掉的函数在list中取到索引,然后通过slice删除对应的元素。就做到了从list中删除某个函数的触发了。

最后看firing跟onStopFalse参数有关。参看fire方法。

3.4  has方法

 <script>
function aaa() {
alert();
}
function bbb(){
alert();
}
var cb=$.Callbacks();
cb.add(aaa);
cb.has(aaa);//true
</script>
has: function( fn ) {
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
//inArray方法传两个参数,作用是fn在数组list里的索引。大于-1当然是存在咯。
//fn不存在比如cb.has(),就返回list和list的长度的逻辑值,自然是true。
//list本来就不存在,那么是false

如果传进去一个函数,那么进行条件判断,返回true或者FALSE。

3.5  empty方法

//从list中删除所有的回调
empty: function() {
list = [];
firingLength = 0;
return this;
},

要清空所有的回调要做两件事情,首先list变为空数组,然后firingLength置为0。

3.6  disable、diabled、lock方法

  // 禁用
disable: function() {
list = stack = memory = undefined;
return this;
},
// 是否被禁用了
disabled: function() {
return !list;
},
// 在当前的状态锁住list
lock: function() {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
// 它是否locked
locked: function() {
return !stack;
},

3.7  fire一系列的方法

首先看fire方法。

第一,我们需要了解的是传进去的data是什么。在回调对象中,供3个地方调用, fire( memory ); 、 fire( args ); 和 fire( stack.shift() ); 。为了简便,通过第三处代码的分析。

第二,stack到底从2903行的 stack = !options.once && [], 发生了什么。这里的意思是如果once是false的话,就会把空数组放到stack中。搜索stack可以看到只是在3023行有一个push动作。

fire = function( data ) {
memory = options.memory && data;
//通过选项的memory返回true或者false
fired = true;
//把fired置为true
firingIndex = firingStart || 0;
//触发的起始索引,要么是firingStart,要么是默认0
firingStart = 0;
firingLength = list.length;
//触发的长度为list的长度
firing = true;
//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
break;
}
}
firing = false;
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
},

接下来看实例方法。

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;
},

最后的fired的意思是是否被触发过了。

// To know if the callbacks have already been called at least once
fired: function() {
return !!fired;
}

jquery的2.0.3版本源码系列(6):2880-3042行,回调对象,对函数的统一管理的更多相关文章

  1. jquery的2&period;0&period;3版本源码系列&lpar;1&rpar;总体结构

    为什么选择2.X版本,而不是1.X版本,因为2.X不兼容IE6/7/8,所以少了兼容代码,让我们更专注于jquery原理的代码. 一共有8830行. 1.1 匿名函数自执行 首先,匿名函数的作用是,把 ...

  2. jquery的2&period;0&period;3版本源码系列&lpar;2&rpar;&colon;21行-94行定义了一些变量和函数 jQuery&equals;function&lpar;&rpar;&lbrace;&rcub;

    2.1.bug通过索引查询 这里的#13335是bug的索引,如何查询呢? 第一步,浏览器地址栏输入"https://bugs.jquery.com/". 第二步,在网页的搜索框里 ...

  3. jquery的2&period;0&period;3版本源码系列&lpar;4&rpar;&colon;285-348行,extend方法详解

    目录 1 . jquery extend的基本使用 通过285行的源码 jQuery.extend = jQuery.fn.extend = function() { ,extend方法要么是直接挂在 ...

  4. jquery的2&period;0&period;3版本源码系列&lpar;3&rpar;&colon;96行-283行,给JQ对象,添加一些方法和属性

    jquery是面向对象的程序,面向对象就离不开方法和属性. 方法的简化 jQuery.fn=jQuery.prototype={ jquery: 版本 constructor: 修正指向问题 init ...

  5. jquery的2&period;0&period;3版本源码系列&lpar;7&rpar;&colon;3043行-3183行,deferred延迟对象,对异步的统一管理

    目录 part1 deferred延迟对象 part2  when辅助方法 网盘源代码 链接: https://pan.baidu.com/s/1skAj8Jj 密码: izta part1 defe ...

  6. jquery的2&period;0&period;3版本源码系列&lpar;5&rpar;&colon;349-817行,extend添加的工具方法

    expando 生成唯一JQ字符串(内部)noconflict避免冲突isReady DOM是否加载完成(DOMContentLoaded)readyReady

  7. jquery源码01---&lpar;2880 &comma; 3042&rpar; Callbacks &colon; 回调对象 &colon; 对函数的统一管理

    // optionsCache : { 'once memory' : { once : true , memory : true } } var optionsCache = {}; // once ...

  8. Kafka 0&period;10&period;1版本源码 Idea编译

    Kafka 0.10.1版本源码 Idea编译 1.环境准备 Jdk 1.8 Scala 2.11.12:下载scala-2.11.12.msi并配置环境变量 Gradle 5.6.4: 下载Grad ...

  9. spring各个版本源码

    各版本源码下载地址 http://maven.springframework.org/release/org/springframework/spring/

随机推荐

  1. 蓝灯github地址

    https://github.com/get*/*/ 闪电联盟蓝灯: http://bbs.sdbeta.com/read-htm-tid-556664.html

  2. mysql修改definer方法

    -- 函数.存储过程 select definer from mysql.proc; update mysql.proc set definer='billing@%';   -- 定时事件 sele ...

  3. 百度编辑器 ueditor &period;net开发

    ueditor1.4.3 下载地址:http://pan.baidu.com/s/1bnCQVtd   <!--editor--> <script type="text/j ...

  4. Html - 幽灵键盘

    Css ;text-align:center} #shurufa_gongneng,#shurufa_num_123,#shurufa_num_456,#shurufa_num_789{} #shur ...

  5. CLH锁 、MCS锁

    一.引文 1.1 SMP(Symmetric Multi-Processor) 对称多处理器结构,指服务器中多个CPU对称工作,每个CPU访问内存地址所需时间相同.其主要特征是共享,包含对CPU,内存 ...

  6. 《转》---使用递归方法DataTable 绑定 TreeView

    转自:http://blog.sina.com.cn/s/blog_8944756d01016yaj.html 前台: <asp:View ID="view0" runat= ...

  7. php大力力 &lbrack;001节&rsqb;2015-08-21&period;php在百度文库的几个基础教程新手上路日记 大力力php 大力同学 2015-08-21 15&colon;28

    php大力力 [001节]2015-08-21.php在百度文库的几个基础教程新手上路日记 大力力php 大力同学 2015-08-21 15:28 话说,嗯嗯,就是我自己说,做事认真要用表格,学习技 ...

  8. webrtc编译之libcommonaudio

    [170/1600] CXX obj/webrtc/common_audio/common_audio.audio_util.o[171/1600] CXX obj/webrtc/common_aud ...

  9. My&lowbar;Plan part1 小结

    数位DP AC十道题目以上 成就达成 八月份!三个月!想想就令人兴奋呢 开始写总结啦 貌似简单的数位DP只需要改改模板就可以啦 就按照我的做题顺序开始总结吧 先是学习了一发模板:http://www. ...

  10. PHP set&lowbar;error&lowbar;handler&lpar;&rpar; 函数

    定义和用法 set_error_handler() 函数设置用户自定义的错误处理函数. 该函数用于创建运行时期间的用户自己的错误处理方法. 该函数会返回旧的错误处理程序,若失败,则返回 null. 语 ...