那么就来读读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. abap 基本知识

    sap gui 安装教程:http://www.itpub.net/forum.php?mod=viewthread&tid=2090890 1:abap 基本数据类型: i(整型),f(浮点 ...

  2. git 生成patch 和打入patch

    转载:https://blog.csdn.net/liuhaomatou/article/details/54410361 平时我们在使用git 管理项目的时候,会遇到这样一种情况,那就是客户使用gi ...

  3. [LeetCode] 181. Employees Earning More Than Their Managers_Easy tag: SQL

    The Employee table holds all employees including their managers. Every employee has an Id, and there ...

  4. python ddt 实现数据驱动一

    ddt 是第三方模块,需安装, pip install ddt DDT包含类的装饰器ddt和两个方法装饰器data(直接输入测试数据) 通常情况下,data中的数据按照一个参数传递给测试用例,如果da ...

  5. Javascript-闰年javascript的判断

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. Mysql的group by语句

    如上图查询结果,因为group by后面的字段是的分组的依据字段,所以select子句中只有是group by后面的字段,或者是聚集函数才有意义.然而mysql居然可以在select子句中出现不在gr ...

  7. JSP—作用域

    application: 用于同一个应用内,所有用户之间的数据共享 作用域: request作用域: 在页面转发,包含中同样有效. <% pageContext.include("te ...

  8. camera原理

    1)Color Filter Array---CFA 图像传感器都采用一定的模式来采集图像数据,常用的有 BGR 模式和 CFA 模式.BGR 模式是一种可直接进行显示和压缩等处理的图像数据模式,它 ...

  9. zw版【转发·台湾nvp系列Delphi例程】HALCON FastThreshold2

    zw版[转发·台湾nvp系列Delphi例程]HALCON FastThreshold2 FastThreshold_Delphi_2.PNG procedure TForm1.Button1Clic ...

  10. ant编译无法依赖rt.jar

    最近同事在用ant编译投产的时候报了一个错误: 程序包com.sun.org.apache.xml.internal.security.exceptions不存在 大致网上搜集了一下资源:具体原因是相 ...