目录jquery
回调的使用有一点像事件绑定,先绑定好,等到有点击事件或者其余时就触发。数组
<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循环来作到。缓存
回调对象的好处是统一管理。app
看一个未使用回调对象的例子:ide
<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>
1.4个选项测试
回调函数有once、memory、unique、stopOnFalse个选项。this
once的做用是只能触发一次。spa
<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的做用是记忆功能。code
<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>
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}
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对象,那么其间定义的方法就能够被调用了。
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>
var start = list.length; console.log(arguments);
这里的打印是代码初始化的过程。
准备工做好了。
脚本代码以下:
function aaa() { alert(1); } function bbb(){ alert(2); } var cb=$.Callbacks(); cb.add(aaa); cb.add(bbb);
这个时候的console.log打印,那么内置arguments对象就分别是aaa和bbb了。
为何会each遍历呢,针对的是 cb.add(aaa,bbb); ,那么argument对象就有了2个元素了。
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最后一个函数了。
// 从触发list里移除某个回调 remove: function() { if ( list ) { jQuery.each( arguments, function( _, arg ) { var index; // 先遍历触发列表,而后经过splice方法裁减掉 while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // 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,其打印结果为
那么接下来就看源码执行了。
经过each方法能够把要remove掉的函数取出来。 ( index = jQuery.inArray( arg, list, index ) ) > -1 是把remove掉的函数在list中取到索引,而后经过slice删除对应的元素。就作到了从list中删除某个函数的触发了。
最后看firing跟onStopFalse参数有关。参看fire方法。
<script> function aaa() { alert(1); } function bbb(){ alert(2); } 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。
//从list中删除全部的回调 empty: function() { list = []; firingLength = 0; return this; },
要清空全部的回调要作两件事情,首先list变为空数组,而后firingLength置为0。
// 禁用 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; },
首先看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; }