那么就来读读jQuery源码的Callbacks部分。

一上来看原版源码

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)
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
list = [],
// Stack of fire calls for repeatable lists
stack = !options.once && [],
// Fire callbacks
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();
}
}
},
// 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 ) {
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 ) {
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;
},
// Remove a callback from the list
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
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;
},
// 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 ) {
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
// Remove all callbacks from the list
empty: function() {
list = [];
firingLength = 0;
return this;
},
// Have the list do nothing anymore
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 ) {
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;
},
// To know if the callbacks have already been called at least once
fired: function() {
return !!fired;
}
}; return self;
};

如你所见,一开始声明了好几个变量,英文注释也说明变量干什么用,但事实上,你还是不知道这些个变量具体作用在哪。

那好,进入http://api.jquery.com/category/callbacks-object/

这里每个api说明,都有个demo,每个demo就相当于一个测试用例。

那么,依据测试用例,给源码设断点,一步步调试,看数据走向。但是一环嵌一环,绕来绕去,都记不住这些个变量变化的意义。

反正,一,句,话。我不知道作者为什么这么写,这个模块的设计思路是怎样。

怎么办,凉拌。

既然有了测试用例,那我就根据它来自己实现。如果实现的过程中,遇到困难,再看看源码,这样就更接近作者的思维,从而知道那些变量,判断的意义所在。

1.首先要实现的就是add和fire功能。

其思路是,引入一个函数管理数组变量,add就是把函数加进数组里,fire就是遍历数组,一个个调用函数。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>add和fire的功能</title>
<script type="text/javascript">
var callbacks = {
callbacks_list: [],
add: function(fn) {
this.callbacks_list.push(fn);
},
fire: function() {
var arg = arguments;
this.callbacks_list.forEach(function(fn) {
fn.apply(this, arg);
})
}
} var foo = function(value) {
console.log("foo: " + value);
}; var bar = function(value) {
console.log("bar: " + value);
}; callbacks.add(foo); callbacks.fire("hello"); callbacks.add(bar); callbacks.fire("world");
</script>
</head>
<body>
现在的callback有add,fire功能
</body>
</html>

2.加入remove功能。

思路是,遍历函数管理数组,判断其成员和传进来的参数是否一致,一致则移除。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>加入remove功能</title>
<script type="text/javascript">
var callbacks = {
callbacks_list: [],
add: function(fn) {
this.callbacks_list.push(fn);
},
fire: function() {
var arg = arguments;
this.callbacks_list.forEach(function(fn) {
fn.apply(this, arg);
})
},
remove: function(fn){
for(var i=0, l = this.callbacks_list.length; i < l; i++){
if(this.callbacks_list[i] == fn){
this.callbacks_list.splice(i,1);
}
}
}
} var foo = function(value) {
console.log("foo: " + value);
}; var bar = function(value) {
console.log("bar: " + value);
}; callbacks.add( foo ); callbacks.fire( "hello" );
callbacks.remove( foo ); callbacks.fire( "world" );
</script>
</head>
<body>
现在的callback有add,fire,remove功能
</body>
</html>

3.加入disable功能。

disable在测试用例中所展示的意思就是,一旦disable了,再怎么fire,也不执行。

内部实现就是,把函数管理数组设置为undefined,然后在fire那边判断undefined,则跳出函数。

我这里忘了在add函数里,判断函数管理数组callbacks_list的undefined。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>3.加入disabled功能</title>
<script type="text/javascript">
var callbacks = {
callbacks_list: [],
add: function(fn) {
this.callbacks_list.push(fn);
},
fire: function() {
if(this.callbacks_list == undefined || this.callbacks_list == null){
return;
}
var arg = arguments;
this.callbacks_list.forEach(function(fn) {
fn.apply(this, arg);
})
},
remove: function(fn) {
for (var i = 0, l = this.callbacks_list.length; i < l; i++) {
if (this.callbacks_list[i] == fn) {
this.callbacks_list.splice(i, 1);
}
}
},
disable: function() {
this.callbacks_list = undefined;
return this;
},
disabled: function() {
return !callbacks_list;
}
}
var foo = function(value) {
console.log(value);
};
callbacks.add(foo); callbacks.fire("foo"); callbacks.disable(); callbacks.fire("foobar");
// foobar isn't output
</script>
</head>
<body>
现在的callback有add,fire,remove,disable功能
</body>
</html>

4.加入has功能

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>4.加入has功能</title>
<script type="text/javascript">
var callbacks = {
callbacks_list: [],
add: function(fn) {
this.callbacks_list.push(fn);
},
fire: function() {
if (this.callbacks_list == undefined || this.callbacks_list == null) {
return;
}
var arg = arguments;
this.callbacks_list.forEach(function(fn) {
fn.apply(this, arg);
})
},
remove: function(fn) {
for (var i = 0, l = this.callbacks_list.length; i < l; i++) {
if (this.callbacks_list[i] == fn) {
this.callbacks_list.splice(i, 1);
}
}
},
disable: function() {
this.callbacks_list = undefined;
return this;
},
disabled: function() {
return !callbacks_list;
},
has: function(fn) {
for (var i = 0, l = this.callbacks_list.length; i < l; i++) {
if (this.callbacks_list[i] == fn) {
return true;
}
}
return false;
}
}
var foo = function(value1, value2) {
console.log("Received: " + value1 + "," + value2);
}; // A second function which will not be added to the list
var bar = function(value1, value2) {
console.log("foobar");
}; // Add the log method to the callbacks list
callbacks.add(foo); // Determine which callbacks are in the list
console.log(callbacks.has(foo));
// true
console.log(callbacks.has(bar));
// false </script>
</head>
<body>
现在的callback有add,fire,remove,disable,has功能
</body>
</html>

5.加入empty功能

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>5.加入empty功能</title>
<script type="text/javascript">
var callbacks = {
callbacks_list: [],
add: function(fn) {
this.callbacks_list.push(fn);
},
fire: function() {
if (this.callbacks_list == undefined || this.callbacks_list == null) {
return;
}
var arg = arguments;
this.callbacks_list.forEach(function(fn) {
fn.apply(this, arg);
})
},
remove: function(fn) {
for (var i = 0, l = this.callbacks_list.length; i < l; i++) {
if (this.callbacks_list[i] == fn) {
this.callbacks_list.splice(i, 1);
}
}
},
disable: function() {
this.callbacks_list = undefined;
return this;
},
disabled: function() {
return !callbacks_list;
},
has: function(fn) {
for (var i = 0, l = this.callbacks_list.length; i < l; i++) {
if (this.callbacks_list[i] == fn) {
return true;
}
}
return false;
},
empty:function(){
this.callbacks_list = [];
return this;
}
}
// A sample logging function to be added to a callbacks list
var foo = function(value1, value2) {
console.log("foo: " + value1 + "," + value2);
}; // Another function to also be added to the list
var bar = function(value1, value2) {
console.log("bar: " + value1 + "," + value2);
}; // Add the two functions
callbacks.add(foo);
callbacks.add(bar); // Empty the callbacks list
callbacks.empty(); // Check to ensure all callbacks have been removed
console.log(callbacks.has(foo));
// false
console.log(callbacks.has(bar));
// false </script>
</head>
<body>
现在的callback有add,fire,remove,disable,has,empty功能
</body>
</html>

6.加入lock功能

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>6.加入lock功能</title>
<script type="text/javascript">
var callbacks = {
callbacks_list: [],
memory: "",
has_locked : false,
add: function(fn) {
this.callbacks_list.push(fn);
},
fire: function() {
if (this.callbacks_list == undefined || this.callbacks_list == null) {
return;
} else {
if(this.has_locked == true){
var callback_this = this;
var arg = callback_this.memory
this.callbacks_list.forEach(function(fn) {
fn.apply(callback_this, arg);
})
}
else{
var arg = arguments;
var callback_this = this;
this.memory = arg;
this.callbacks_list.forEach(function(fn) {
fn.apply(callback_this, arg);
})
} } },
remove: function(fn) {
for (var i = 0, l = this.callbacks_list.length; i < l; i++) {
if (this.callbacks_list[i] == fn) {
this.callbacks_list.splice(i, 1);
}
}
},
disable: function() {
this.callbacks_list = undefined;
return this;
},
disabled: function() {
return !callbacks_list;
},
has: function(fn) {
for (var i = 0, l = this.callbacks_list.length; i < l; i++) {
if (this.callbacks_list[i] == fn) {
return true;
}
}
return false;
},
empty: function() {
this.callbacks_list = [];
return this;
},
lock: function() {
this.callbacks_list = [];
this.has_locked = true;
}
}
/*demo1*/
var foo = function(value) {
console.log("foo:" + value);
};
var bar = function(value) {
console.log("bar:" + value);
}; callbacks.add(foo); callbacks.fire("hello"); callbacks.lock(); callbacks.fire("world"); /*demo2*/
// Add the foo function to the callback list again
callbacks.add(foo); // Try firing the items again
callbacks.fire("silentArgument");
// Outputs "foo: hello" because the argument value was stored in memory // Add the bar function to the callback list
callbacks.add(bar); callbacks.fire("youHadMeAtHello"); </script>
</head>
<body>
现在的callback有add,fire,remove,disable,has,empty,lock功能
</body>
</html>

还有未完成的就是,$.Callbacks()的参数once,memory,unique,stopOnFalse。

我一直思考的是,代码构建过程和纯逻辑思路,代码阅读。

一般来说,一个库的形成,是因为作者编写的过程中,发现一个个需求,或者一个个bug,然后需求和bug形成测试用例,根据测试用例,构造库。

它的库代码最后为什么会这个样子,一次次引入新变量,一个个判断来嵌套,还有为了库的使用便利,对函数参数做了各种判断处理,导致直接阅读有难度,不能通俗易懂。

那我们能否根据测试用例提取出这个模块的纯逻辑思路。至于逻辑思路的实现,交给开发自己代码能力实现。

读jQuery源码有感2的更多相关文章

  1. 读jQuery源码有感

    读之前的预备工作: 1.基础的js知识,以及html和css知识,和正则表达式知识.可以参考妙味课堂的基础js,html和css大纲. 2.JavaScript核心指南的知识http://www.cn ...

  2. 读jQuery源码有感3

    这次的主题是,具体的库和抽象的思路. 当看到Deferred这个区块时,觉得jQuery代码设计挺复杂,得用许多脑力才能看明白. 可是把这个峰回路转十八回的代码看懂又如何,是为了使用过程中出现bug后 ...

  3. 【读jQuery源码有感系列一】callee

    <script type="text/javascript"> /*调用自身*/ function calleeDemo() { try{ } catch (error ...

  4. 读jQuery源码 - Deferred

    Deferred首次出现在jQuery 1.5中,在jQuery 1.8之后被改写,它的出现抹平了javascript中的大量回调产生的金字塔,提供了异步编程的能力,它主要服役于jQuery.ajax ...

  5. 读jQuery源码之整体框架分析

    读一个开源框架,大家最想学到的就是设计的思想和实现的技巧.最近读jQuery源码,记下我对大师作品的理解和心得,跟大家分享,权当抛砖引玉. 先附上jQuery的代码结构. (function(){ / ...

  6. 【读fastclick源码有感】彻底解决tap“点透”,提升移动端点击响应速度

    申明!!!最后发现判断有误,各位读读就好,正在研究中.....尼玛水太深了 前言 近期使用tap事件为老夫带来了这样那样的问题,其中一个问题是解决了点透还需要将原来一个个click变为tap,这样的话 ...

  7. 读jQuery源码 - Callbacks

    代码的本质突出顺序.有序这一概念,尤其在javascript——毕竟javascript是单线程引擎. javascript拥有函数式编程的特性,而又因为javascript单线程引擎,我们的函数总是 ...

  8. 读jQuery源码释疑笔记2

    本释疑笔记是针对自己在看源码的过程中遇到的一些问题的解答,对大众可能不具有参考性,不过可以看看有没有你也不懂得地方,相互学习,相互进步. 1.函数init <div id="one&q ...

  9. 读jQuery源码释疑笔记

    本释疑笔记是针对自己在看源码的过程中遇到的一些问题的解答,对大众可能不具有参考性,不过可以看看有没有你也不懂得地方,相互学习,相互进步.  1.each的用法 之前对each的用法一直迷迷糊糊,这次终 ...

随机推荐

  1. ubuntu设置目录容量大小

    1:方法如下 sudo dd if=/dev/zero of=/root/disk1.img bs=2M count=10      //          2M*10=20M    zero 是de ...

  2. OS-96

    print('os.access(path,mode):检验权限模式----------------------------------------------------------------') ...

  3. Amazon SES SPF和DKIM设置教程

    SPF和DKIM设置是争对域名邮箱而言的(公共邮件也不会给你修改DNS的权限),主要作用就是防止邮箱伪造提升邮件信用度 首先到亚马逊添加域名并验证 添加后,给出了域名验证的方法,就是在dns记录里添加 ...

  4. sklearn_Logistic Regression

    一.什么是逻辑回归? 一种名为“回归”的线性分类器,其本质是由线性回归变化而来的,一种广泛使用于分类问题中的广义回归算法 面试高危问题:Sigmoid函数的公式和性质 Sigmoid函数是一个S型的函 ...

  5. VMware coding Challenge

    思路:这道题要观察,举个例子,1 2 * * 3 * 4  5 * * 6 7 * 8 * *, 用Stack,先序遍历,遇到数字就入栈,如果遇到 * *,说明栈顶节点是叶子节点,一条根到叶子的路径这 ...

  6. BCB 串口控件的使用 TComm

    昨天工作用到了串口通信,MMP的,昨天懵逼了一下午,今天终于整通了,身为菜鸟,大师们是不懂这些心痛的. 进入主题:使用BCB提供的控件TComm编程方便且简单,TComm位于System分类里面.   ...

  7. 2:4 动态方法的调用(简化Action的配置)

    动态方法的第一种方法: 所以我们要手动设置 动态调用的开关打开:strus-core-jar里面:修改常量 使用方法: 根据请求来区分用哪个方法处理,处理完了,注意要在该方法里面返回与请求相同的字符串 ...

  8. jQuery delay() 方法

    定义和用法 delay() 方法对队列中的下一项的执行设置延迟. 语法 $(selector).delay(speed,queueName) 参数 描述 speed 可选.规定延迟的速度. 可能的值: ...

  9. Hive sql和Presto sql的一些对比

    最近由于工作上和生活上的一些事儿好久没来博客园了,但是写博客的习惯还是得坚持,新的一年需要更加努力,困知勉行,终身学习,每天都保持空杯心态.废话不说,写一些最近使用到的Presto SQL和Hive ...

  10. JVisualVM远程监控

    本文主要转自:http://blog.iamzsx.me/show.html?id=394002 http://www.wl566.com/wz/64022.html 1. 通过JMX方式监控远程li ...