您的位置:首页 > Web前端 > JQuery

jquery3.0源码解读(四)Callbacks

2016-09-13 20:54 399 查看
本来想先分析defer的,但是在defer的文件中,发现它依赖于另外一个比较重要的模块callbacks。于是,这节就先分析callbacks吧。

前置知识

观察者模式

钩子

递延

应用场景

在js开发中,由于没有多线程,经常会遇到回调这个概念,比如说,在 ready 函数中注册回调函数,注册元素的事件处理等等。在比较复杂的场景下,当一个事件发生的时候,可能需要同时执行多个回调方法,可以直接考虑到的实现就是实现一个队列,将所有事件触发时需要回调的函数都加入到这个队列中保存起来,当事件触发的时候,从这个队列中依次取出保存的函数并执行。

功能说明

callbacks指一个多用途的回调列表对象,提供了强大的的方式来管理回调函数列表。简单的使用方式如下:

function fn1( value ){
console.log( value );
}

function fn2( value ){
fn1("fn2 says:" + value);
return false;
}

var callbacks = $.Callbacks();
callbacks.add( fn1 );
callbacks.fire( "foo!" ); // outputs: foo!

callbacks.add( fn2 );
callbacks.fire( "bar!" ); // outputs: bar!, fn2 says: bar!


简单来说就是管理回调函数的执行的。这个模块主要还是提供给jquery内部的ajax和defer使用。

$.callbacks(flags)

$.callbacks(flags)中的flags是$.Callbacks()的一个可选参数, 结构为一个用空格标记分隔的标志可选列表,用来改变回调列表中的行为 (比如. $.Callbacks( ‘unique stopOnFalse’ ))。

可用的 flags:

once: 确保这个回调列表只执行一次

memory: 当队列已经触发之后,再添加进来的函数就会直接被调用(并且使用的是上一次fire的参数),不需要再触发一次。

unique: 保证函数的唯一。

stopOnFalse: 当一个回调返回false 时中断调用

方法说明

callbacks.add() 回调列表中添加一个回调或回调的集合。

callbacks.disable() 禁用回调列表中的回调

callbacks.disabled() 确定回调列表是否已被禁用。

callbacks.empty() 从列表中删除所有的回调.

callbacks.fire() 用给定的参数调用所有的回调

callbacks.fired() 访问给定的上下文和参数列表中的所有回调。

callbacks.fireWith() 访问给定的上下文和参数列表中的所有回调。

callbacks.has() 确定列表中是否提供一个回调

callbacks.lock() 锁定当前状态的回调列表。

callbacks.locked() 确定回调列表是否已被锁定。

callbacks.remove() 从回调列表中的删除一个回调或回调集合。

源码分析

源码如下:

jQuery.Callbacks = function( options ) {

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

var firing,

memory,

fired,

locked,

list = [],

queue = [],

firingIndex = -1,

fire = function() {

locked = options.once;

fired = firing = true;
for ( ; queue.length; firingIndex = -1 ) {
memory = queue.shift();
while ( ++firingIndex < list.length ) {

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

firingIndex = list.length;
memory = false;
}
}
}

if ( !options.memory ) {
memory = false;
}

firing = false;

if ( locked ) {

if ( memory ) {
list = [];

} else {
list = "";
}
}
},

self = {

add: function() {
if ( list ) {

if ( memory && !firing ) {
firingIndex = list.length - 1;
queue.push( memory );
}

( function add( args ) {
jQuery.each( args, function( _, arg ) {
if ( jQuery.isFunction( arg ) ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {

// Inspect recursively
add( arg );
}
} );
} )( arguments );

if ( memory && !firing ) {
fire();
}
}
return this;
},

remove: function() {
jQuery.each( arguments, function( _, arg ) {
var index;
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );

// Handle firing indexes
if ( index <= firingIndex ) {
firingIndex--;
}
}
} );
return this;
},

has: function( fn ) {
return fn ?
jQuery.inArray( fn, list ) > -1 :
list.length > 0;
},

empty: function() {
if ( list ) {
list = [];
}
return this;
},

disable: function() {
locked = queue = [];
list = memory = "";
return this;
},
disabled: function() {
return !list;
},

lock: function() {
locked = queue = [];
if ( !memory && !firing ) {
list = memory = "";
}
return this;
},
locked: function() {
return !!locked;
},

fireWith: function( context, args ) {
if ( !locked ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
queue.push( args );
if ( !firing ) {
fire();
}
}
return this;
},

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

fired: function() {
return !!fired;
}
};

return self;
};


下面,我们就直接从构造方法和返回self中提供的操作方法,按照上面的操作方法列表顺序依次分析。

$.callbacks(flags)

取出了options(flag)并保存,并定义了若干参数,分别是:

firing:是否正在fire触发阶段,用来判断是外部的触发,还是回调函数内部的嵌套触发

memory:记录上次触发时使用的参数(相当于执行环境)

fired:记录是否已经被触发过至少一次

locked:锁定外部fire相关接口

list:回调列表

queue:多次fire调用(因为可能被嵌套调用)的调用参数列表(相当于执行环境列表)

firingIndex:回调列表list的触发索引,也会用在指定add递延触发位置

还有一个fire内部方法,这个比较重要,它的执行过程如下:

首先,locked = options.once;也就是说,如果flag中有once选项,那么只要执行过fire,下一次就会锁住。

接着,fired = firing = true;标示正在执行fire。

之后,循环从queue中取出执行执行环境,赋值给memory(保存了当前执行环境)。并不断顺序执行列表中的回调函数。期间还判断了stopOnFalse选项。当回调返回false时,并且有stopOnFalse选项,就会销毁memory,相当于memory选项没有了。

再接着,判断options.memory是否定义,如果没有,同样销毁memory(上一次的执行环境)。

最后,判断锁定,如果锁定,外部fire就不用了,由是否有递延指定add(会调用内部fire)是否可用,无递延就要disable掉(locked+list)。

最后,返回self,self中暴露了各种操作方法,如下。

callbacks.add()

首先,判断list(回调函数列表),实现了once功能。在上述fire函数中,如果有once标志,list将被赋值成空。

接着,如果有memory(记录着上次触发时使用的参数),并且不在执行的话,保存firingIndex(回调列表list的触发索引,也会用在指定add递延触发位置),并且queue(多次fire调用(因为可能被嵌套调用)的调用参数列表)存入memory。

接着,把add的回调函数,压入list,这里支持递归add。

最后,如果不在firing中,并且是memory模式,则执行fire,由于之前queue中压入了memory,并记录了firingIndex,所以fire会执行刚刚被add进list的回调函数。如果在firing中,当前的函数,自然也会被执行。

callbacks.disable()

这个比较简单,把locked ,queue,list,memory都清空,add将无法使用,fire同样不行。

callbacks.disabled()

直接返回!list。

callbacks.empty()

清空list 。

callbacks.fire()

直接调用了fireWith,并传入了参数。

callbacks.fired()

返回!!fired(双感叹号指强制转换成布尔值)。

callbacks.fireWith()

判断是否locked,如果不是,把参数格式化成[环境,参数数组],写进queue。并执行fire。

callbacks.has()

判断是否有指定回调,无参数则判断回调列表是否空。

callbacks.lock()

无递延(每次执行完memory重置为false)或没触发过,则直接禁用;否则还是可以相应add后的递延函数。

callbacks.locked()

返回!!locked。

callbacks.remove()

移除回调,支持多参数。去掉所有相同回调,当回调内调用remove时,若删除项为已执行项,修正了firingIndex位置。

参考

http://www.cnblogs.com/aaronjs/p/3342344.html

http://www.cnblogs.com/haogj/p/4473477.html

http://blog.csdn.net/vbdfforever/article/details/50986673

http://www.cnblogs.com/yangjunhua/p/3509342.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息