jQuery 源码细读 -- $.Callbacks
$.Callbacks 是 jQuery 提供的可以方便地处理各种回调(callback)列表的类,其源代码是闭包的经典实现。
基本原理就是通过在闭包环境内保存一个 list = [] 数组用于存储回调列表,并用 firing,firingStart,firingLength,firingIndex等标志位来控制闭包的有序执行,下面是最重要的2个内部函数,触发函数 fire 和 添加函数 add。
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 add
break;
}
}
firing = false;
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
}
fire 函数
add: function () {
if ( list ) {
// First, we save the current length
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" ) {
// Inspect recursively
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
if (firing) {
console.log('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;
}
add 函数
引起我思考的是遍历回调列表时, firing 标志位的使用,遍历前赋值 firing = true,遍历完赋值 firing = false。在 add 函数内如果检查到 firing === true,则将回调函数加入到还没遍历完的列表末端即可。
可是问题来了,什么情况下会出现for 循环没执行完的情况下 add 函数被调用呢?即 add 函数执行时 firing 有没可能为 true?
我在 add 函数碰上 firing === true 时加上一句调试 console.log('firing')
1. 第一种情况,在某个回调函数内部再嵌套添加另一个回调。代码如下:
var callBacks = $.Callbacks();
callBacks.add(function () {
console.log('callBacks 0'); callBacks.add(function () {
console.log('callBacks 2');
});
}); callBacks.add(function () {
console.log('callBacks 1');
}); callBacks.fire();
//执行结果
//callBacks 0
//firing
//callBacks 1
//callBacks 2
还有没其他的可能呢?js 不是单线程的吗?是不是只要在每个回调函数内不再调用 callBacks.add 就不会碰上 firing === true 呢?
2. 利用浏览器对页面事件的响应处理
<input id="text1" type="text" />
<script type="text/javascript">
var text1 = document.getElementById('text1'); text1.onblur = function () {
console.log('.onblur() is called'); callBacks.add(function () {
console.log('callBacks 2');
});
}; text1.focus(); var callBacks = $.Callbacks(''); callBacks.add(function () {
console.log('callBacks 0');
text1.blur();
console.log('.blur() is called');
}); callBacks.add(function () {
console.log('callBacks 1');
}); callBacks.fire();
//IE下执行结果
//callBacks 0
//.blur() is called
//callBacks 1
//.blur() is called //非IE浏览器下执行结果
//callBacks 0
//.onblur() is called
//firing
//.blur() is called
//callBacks 1
//callBacks 2
</script>
这就是浏览器处理事件流程时带来的 js 执行流混乱。
在非 IE 下 .blur() 的调用会使当前执行堆栈挂起,转而执行 onblur 事件的回调函数,等 onblur处理完了才回头执行 .blur() 后面的代码。
IE则会等 .blur() 所在的上下文执行完之后才执行 onblur 事件的回调函数。
总结就是,浏览器无法保证 JavaScript 的单线程线性执行。详细可以看看这篇文章
Is javascript guaranteed to be single-threaded?
里面还讲到一个有趣的点,不要认为 alert 函数会挂起这个js 执行,window.onresize 事件在这种情况下还是可以被触发的,Linux 很容易, window 下可以通过改变分辨率的方式做到。
最后,我开始思考另一个问题,脱离了浏览器的 nodeJs 会出现这种问题吗?nodeJs能保证一个函数的执行不因为另一个函数而挂起吗?
jQuery 源码细读 -- $.Callbacks的更多相关文章
- JQuery源码解析--callbacks
(function (global, factory) { factory(global); })(this, function (window, noGlobal) { var rootjQuery ...
- 读jQuery源码 - Deferred
Deferred首次出现在jQuery 1.5中,在jQuery 1.8之后被改写,它的出现抹平了javascript中的大量回调产生的金字塔,提供了异步编程的能力,它主要服役于jQuery.ajax ...
- 读jQuery源码有感2
那么就来读读jQuery源码的Callbacks部分. 一上来看原版源码 jQuery.Callbacks = function( options ) { // Convert options fro ...
- 读jQuery源码 - Callbacks
代码的本质突出顺序.有序这一概念,尤其在javascript——毕竟javascript是单线程引擎. javascript拥有函数式编程的特性,而又因为javascript单线程引擎,我们的函数总是 ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- jQuery源码 Ajax模块分析
写在前面: 先讲讲ajax中的相关函数,然后结合函数功能来具体分析源代码. 相关函数: >>ajax全局事件处理程序 .ajaxStart(handler) 注册一个ajaxStart事件 ...
- jQuery源码笔记(一):jQuery的整体结构
jQuery 是一个非常优秀的 JS 库,与 Prototype,YUI,Mootools 等众多的 Js 类库相比,它剑走偏锋,从 web 开发的实用角度出发,抛除了其它 Lib 中一些中看但不实用 ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- 读Zepto源码之Callbacks模块
Callbacks 模块并不是必备的模块,其作用是管理回调函数,为 Defferred 模块提供支持,Defferred 模块又为 Ajax 模块的 promise 风格提供支持,接下来很快就会分析到 ...
随机推荐
- stuff about set multiset map multimap
A lot of interviewers like to ask the candidates the difference between set and multiset(map and mul ...
- [转]mysql 5.6 存储过程+事务+游标+错误异常抛出+日志写入
转自:http://www.wolonge.com/post/detail/118249 DELIMITER $$ USE `ecstore`$$ DROP PROCEDURE IF EXISTS ` ...
- MVC Controller return 格式
所看到的Action都是return View();我们可以看作这个返回值用于解析一个aspx文件.而它的返回类型是ActionResult如 public ActionResult Index() ...
- swift Array 数组
// // main.Swift // swift数组 // // Created by zhangbiao on 14-6-15. // Copyright (c) 2014年 理想. Al ...
- linux安装php出现的各种错误解决方案
configure: error: Try adding --with-zlib-dir=<DIR>. Please check config.log for more informati ...
- Jquery调用从ashx文件返回的jsonp格式的数据处理实例
开发环境:vs2010+jquery-1.4.min.js 解决问题:网上代码比较少,好多调试不通,返回数据不用json而用jsonp主要考虑解决跨域问题 开发步骤:打开VS2010,新建一web站点 ...
- MegaCli监控RAID磁盘健康信息
首先有必要先介绍一下有关RAID卡的使用,有关Cache问题也有个人理解:生产系统上的物理机磁盘配置大多都是RAID5,之前会经常性的遇到磁盘IO在某个时间极度的跟不上节奏,稍微写入量,磁盘utils ...
- 根据子级ID获取其所有父级
当前位置>新闻中心>行业资讯 CLASSID CLASSNAME CLASSPID 1 新闻中心 0 2 行业资讯 1 3 公司新闻 1 4 媒体聚焦 0 当前位置<%=navNam ...
- sql like '%x%'优化
好久没写点什么了.唉(此处省略无数,一切尽在苦逼中...) 说说sql中的全匹配优化吧.在sql server进行模糊查询的时候,如果是进行全匹配的话,那么肯定会用到like.我们知道like '%张 ...
- everything is nothing
可以选ios,可以选择android ,可以选择javaee,因为不想让自己这段时间的努力没有一个完美的结局.最起码真的能做点东西出来,所以6.10--8.10,两个月把javaee实训的该准备.可以 ...