jquery中的callbacks之我见
callbacks是jquery的核心之一。
语法如下:
jQuery.Callbacks( flags ) flags 类型: String 一个用空格标记分隔的标志可选列表,用来改变回调列表中的行为。
once
: 确保这个回调列表只执行( .fire() )一次(像一个递延 Deferred).
memory
: 保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调 (像一个递延 Deferred).
unique
: 确保一次只能添加一个回调(所以在列表中没有重复的回调).
stopOnFalse
: 当一个回调返回false 时中断调用
使用案例:
function fn1(val) {
console.log('fn1 says ' + val);
}
function fn2(val) {
console.log('fn2 says ' + val);
return false;
}
var cbs = $.Callbacks('once memory');
cbs.add(fn1);
//第一次fire会缓存传入的参数
cbs.fire('foo'); //fn1 says foo
//fire过一次之后,以后的add都会自动调用fire,传入的参数是上次fire传入的'foo'
cbs.add(fn2); //fn2 says foo
function fn1(val) {
console.log('fn1 says ' + val);
}
function fn2(val) {
console.log('fn2 says ' + val);
return false;
}
var cbs = $.Callbacks('stopOnFalse');
cbs.add(fn2)
cbs.add(fn1);
cbs.fire('foo'); //fn2 says foo
function fn1(val) {
console.log('fn1 says ' + val);
}
function fn2(val) {
console.log('fn2 says ' + val);
return false;
}
var cbs = $.Callbacks('once');
cbs.add(fn2)
cbs.add(fn1);
cbs.fire('foo'); //fn2 says foo fn1 says foo cbs.add(fn2);
cbs.fire("bar"); //不输出
function fn1(val) {
console.log('fn1 says ' + val);
}
function fn2(val) {
console.log('fn2 says ' + val);
return false;
}
var cbs = $.Callbacks('stopOnFalse');
cbs.add(fn2)
cbs.add(fn1);
cbs.fire('foo'); //fn2 says foo cbs.add(fn2);
cbs.fire("bar"); //fn2 says bar
源码分析:
// String to Object options format cache
var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache
//20170618 huanhua 参数options="once memory"
// 返回结果: optionsCache[once memory]={once:true,memory:true}
function createOptions( options ) {
var object = optionsCache[options] = {};
//20170618 huanhua 前面已经定义正则 core_rnotwhite= /\S+/g , options.match( core_rnotwhite )返回数组["once","memory"]类似这种
jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true;
});
return object;
} /*
* Create a callback list using the following parameters:
*
* options: an optional list of space-separated options that will change how
* the callback list behaves or a more traditional option object
*
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
*
* Possible options:
*
* once: will ensure the callback list can only be fired once (like a Deferred)
*
* memory: will keep track of previous values and will call any callback added
* after the list has been fired right away with the latest "memorized"
* values (like a Deferred)
*
* unique: will ensure a callback can only be added once (no duplicate in the list)
*
* stopOnFalse: interrupt callings when a callback returns false
*
*/
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 ); var // Flag to know if list is currently firing
firing,
// Last fire value (for non-forgettable lists)
//20170618 huanhua 存储的是最后一次 fire传递的参数值
memory,
// Flag to know if list was already fired
fired,
// End of the loop when firing
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
// First callback to fire (used internally by add and fireWith)
firingStart,
// Actual callback list
// 20170618 huanhua 回调函数的存储列表
list = [],
// Stack of fire calls for repeatable lists
// 20170618 huanhua 回调函数不是只执行一次的时候,用 stack保存正在执行回调的时候,再次请求执行的 fire()中传入的参数
stack = !options.once && [],
// Fire callbacks
fire = function (data) {//20170618 data是一个数组[context,arg],第一个是执行的上下午,第二个是真正的参数
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for (; list && firingIndex < firingLength; firingIndex++) {
//20170618 huanhua 判断
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
// 阻止未来可能由于add所产生的回调,add中有这段代码
//else if ( memory ) {
// firingStart = start; //20170618 huanhua 从列表最新添加的回调函数位置开始执行
// fire( memory ); //20170618 huanhua 立即执行回调函数
//}
memory = false; // To prevent further calls using add
break;//由于参数stopOnFalse为true,所以当有回调函数返回值为false时退出循环
}
}
firing = false;
if (list) {
//20170618 huanhua 处理正在执行回调中,再次触发的 fire(),也就是同时第一个触发的 fire还没执行完,紧接着多次执行了 fire
//在 fireWith 中有这个代码:if ( firing ) {stack.push(args);}
if (stack) {//20170619 huanhua 只要没有执行 disable和lock(stack=undefined) 或者 options不包含once 就会执行这里
if ( stack.length ) {
fire( stack.shift() );
}
} else if (memory) { //20170619 huanhua 只有在 options = "once memory" 的时候才会执行这里
list = [];
} else {//20170619 huanhua options 等于 ["once","once stopOnFalse","once union"]中的任何一个的时候才会执行这里
self.disable();
}
}
},
// Actual Callbacks object
self = {
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
// First, we save the current length
var start = list.length;
(function add(args) {
//20170618 huanhua add(fun1,fun2,fun3)
jQuery.each( args, function( _, arg ) {
var type = jQuery.type(arg);
//20170618 huanhua 判断是函数
if (type === "function") {
//20170618 huanhua !options.unique判断 当$.Callbacks('unique')时,保证列表里面不会出现重复的回调
//!self.has( arg )只要不列表里不存在,就添加到列表
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
//20170618 huanhua 判断传递的参数是类似数组的对象,或者就是数组
// add({leng:2,0:fun1,1:fun2},{leng:1,0:fun3});
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
//20170618 huanhua 当fire执行的时间需要比较长的时候,我们在执行 add的时候,fire也在执行,add执行完后,fire还没执行完,为了防止新加的不执行,
//所以重新赋值了需要执行的回调函数个数 firingLength = list.length;
if ( firing ) {
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
//20170618 huanhuaa $.CallBacks("memory"),并且还调用了 fire
} else if ( memory ) {
firingStart = start; //20170618 huanhua 从列表最新添加的回调函数位置开始执行
fire( memory ); //20170618 huanhua 立即执行回调函数
}
}
return this;
},
// Remove a callback from the list
//20170618 huanhua 删除指定的回调函数
remove: function () {
//20170618 huanhua 判断回调函数列表是有效的
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
//20170618 huanhua index = jQuery.inArray( arg, list, index )获取需要被删除的回调函数在列表中的位置
while ((index = jQuery.inArray(arg, list, index)) > -1) {
//20170618 huanhua 删除回调函数
list.splice( index, 1 );
// Handle firing indexes
//20170618 huanhua 如果删除的时候在执行 fire()
if (firing) {
//20170618 huanhua 当被删除的回调函数的位置 <= 回调函数列表总长度的时候,删除了一个回调函数,所以 firingLength 要减一个了
if ( index <= firingLength ) {
firingLength--;
}
//20170618 huanhua 当被删除的回调函数的位置 <= 执行到的回调函数的位置时候,删除了一个回调函数,所以 firingIndex 要减一个了
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
// Check if a given callback is in the list.
// If no argument is given, return whether or not list has callbacks attached.
has: function (fn) {//回调函数是否在列表中.
//20170616 huanhua !!( list && list.length ) 如果执行的 has("")/has(undefined)等等,如果 执行了 def.disable(),list就等于undefined了
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
// Remove all callbacks from the list
//20170618 huanhua 清空回调函数列表
empty: function() {
list = [];
return this;
},
// Have the list do nothing anymore
//20170618 huanhua 禁用回调列表中的回调。
disable: function() {
list = stack = memory = undefined;
return this;
},
// Is it disabled?
disabled: function() {
return !list;
},
// Lock the list in its current state
lock: function() {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
// Is it locked?
locked: function() {
return !stack;
},
// Call all callbacks with the given context and arguments
fireWith: function( context, args ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
if (list && (!fired || stack)) {
//20170618 huanhua 如果正在执行回调函数
//将参数推入堆栈,等待当前回调结束再调用
if ( firing ) {
stack.push(args);
//20170618 huanhua 如果当前不是正在执行回调函数,就直接执行
} else {
fire( args );
}
}
return this;
},
// Call all the callbacks with the given arguments
fire: function() {
self.fireWith( this, arguments );
return this;
},
// To know if the callbacks have already been called at least once
fired: function() {
return !!fired;
}
}; return self;
};
jquery中的callbacks之我见的更多相关文章
- 深入jQuery中的Callbacks()
引入 初看Callbacks函数很不起眼,但仔细一瞅,发现Callbacks函数是构建jQuery大厦的无比重要的一个基石.jQuery中几乎所有有关异步的操作都会用到Callbacks函数. 为什么 ...
- 深入理解jQuery中的Deferred
引入 1 在开发的过程中,我们经常遇到某些耗时很长的javascript操作,并且伴随着大量的异步. 2 比如我们有一个ajax的操作,这个ajax从发出请求到接收响应需要5秒,在这5秒内我们可以 ...
- 读jQuery源码 - Callbacks
代码的本质突出顺序.有序这一概念,尤其在javascript——毕竟javascript是单线程引擎. javascript拥有函数式编程的特性,而又因为javascript单线程引擎,我们的函数总是 ...
- JQuery中的回调对象
JQuery中的回调对象 回调对象(Callbacks object)模块是JQuery中的一个很基础的模块,很多其他的模块(比如Deferred.Ajax等)都依赖于Callbacks模块的实现.本 ...
- js便签笔记(6)——jQuery中的ready()事件为何需要那么多代码?
前言: ready()事件的应用,是大家再熟悉不过的了,学jQuery的第一步,最最常见的代码: jQuery(document).ready(function () { }); jQuery(fun ...
- jQuery源码分析-jQuery中的循环技巧
作者:nuysoft/JS攻城师/高云 QQ:47214707 EMail:nuysoft@gmail.com 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 前记:本文收集了jQuery ...
- 详解jQuery中 .bind() vs .live() vs .delegate() vs .on() 的区别
转载自:http://zhuzhichao.com/2013/12/differences-between-jquery-bind-vs-live/ 我见过很多开发者很困惑关于jQuery中的.bin ...
- Jquery中.ajax和.post详解
之前写过一篇<.NET MVC 异步提交和返回参数> ,里面有一些ajax的内容,但是不深入,这次详细剖析下jquery中$.ajax的用法. 首先,上代码: jquery-1.5.1 $ ...
- jquery中的$.ajax()的源码分析
针对获取到location.href的兼容代码: try { ajaxLocation = location.href; } catch( e ) { // Use the href attribut ...
随机推荐
- 九度OJ-第5章-图论
二.并查集 1. 例题 题目1012:畅通工程 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:10519 解决:4794 题目描述: 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出 ...
- 【Java】字节数组转换工具类
import org.apache.commons.lang.ArrayUtils; import java.nio.charset.Charset; /** * 字节数组转换工具类 */ publi ...
- python 转义字符 html 爬虫
用python的requests包 抓取某些网页时,返回的html中,一些字段含有一些 转义字符 \\\\\\\ 这些转义字符给我们后期处理带来一些麻烦, 比方说 运行js等 python用print ...
- 一款我常用到的手机app
我从初中开始接触电子书,后来渐渐养成了看电子书的习惯.在阅读电子书的过程中自然要接触到各种各样的阅读类的手机app,比如书旗.qq阅读.百度阅读器等等.个人感觉掌阅使用起来好一些. 首先,它的界面很简 ...
- 使用maven-tomcat7-plugins时调试出现source not found解决
直接看下面的步骤: 步骤1: 步骤2: 步骤3: 步骤4:
- 我发起了一个 网格计算 协议 开源项目 GridP
GridP 是 Grid Protocol 的 全称 . 我在 <关于软件产业的两个契机> https://www.cnblogs.com/KSongKing/p/95319 ...
- Python脚本模拟僵尸进程与孤儿进程
最近一台机器的systemd内存高达30%多,一直不变,后来排查是僵尸进程,什么是僵尸进程呢,只能google,百度等先了解,然后自己总结了一下,虽然这是基础的东西,但是对于我来说就如新大陆一样.花了 ...
- 关于Java流
- Kubernetes Kubelet安全认证连接Apiserver
Kubelet使用安全认证连接Apiserver,可以用Token或证书连接.配置步骤如下. 1,生成Token命令 head -c /dev/urandom | od -An -t x | tr - ...
- 使用docker查看jvm状态,在docker中使用jmap,jstat
Docker中查看JVM的信息: 1. 列出docker容器:docker ps 2. 标准输入和关联终端:docker exec -it 容器ID bash 3. 查找出j ...