学习jQuery.Callbacks

本文详细解读jQuery Callbacks的实现原理与源码细节,包括如何使用Callbacks、其内部构造与执行流程,并通过代码解释重点方法add和fire的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://www.cnblogs.com/littledu/articles/2811728.html

Callbacks是JQ的一个回调对象,可以用来添加回调,执行回调,删除回调等等。并提供一些参数如once,memory,unique等来进行特殊需求的控制。这里就不举例说明Callbacks的用法了。具体详细说明可以参见:http://api.jquery.com/jQuery.Callbacks/

我们学习源码,需先了解如何使用,这里假设我们已经知道如何使用Callbacks了。

他的实现思路就是: 构建一个存放回调的数组,如var list = [],通过闭包使这条回调数组保持存在。添加回调时,将回调push进list,执行则遍历list执行回调。

看思路貌似很简单,我们就直接来看源码吧,对于源码的阅读,我的习惯是大概过一次源码,看看注释了解下大概是作何功用,再直接运行最简单的示例,跟踪下它的执行流程,这个过程中各变量的变化,返回值等等。

这里,在过源码的时候,我们就知道当调用了var cal = $.Callbacks()的时候,返回来的是一个对象(self),这个对象里面实现了add,remove,fire等方法,也就是说,可以用cal.add(fn)来添加回调,cal.remove()来删除回调。

好吧,直接上代码解释,只解释了add和fire两个重要方法,其他都较简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// String to Object options format cache
var  optionsCache = {};
   
// Convert String-formatted options into Object-formatted ones and store in cache
function  createOptions( options ) {
     var  object = optionsCache[ options ] = {};
     jQuery.each( options.split( core_rspace ), 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 这里设计为一个对象,这一步就是先判断下options缓存里有没这个对象存在,有直接拿来用,没有则通过createOptions方法将传过来的字符串转化为一个对象,对象形式如下:
     // options = { 'once': true,'memory': true}
     options = typeof  options === "string"  ?
         ( optionsCache[ options ] || createOptions( options ) ) :
         jQuery.extend( {}, options );
   
     var  // Last fire value (for non-forgettable lists)
         memory,
         // Flag to know if list was already fired
         fired,
         // Flag to know if list is currently firing
         firing,
         // First callback to fire (used internally by add and fireWith)
         firingStart,
         // End of the loop when firing
         firingLength,
         // Index of currently firing callback (modified by remove if needed)
         firingIndex,
         // Actual callback list
         list = [],
         // Stack of fire calls for repeatable lists
         stack = !options.once && [],
         // Fire callbacks
         fire = function ( data ) {
             memory = options.memory && data;  //当$.Callbacks('memory')时,保存data,data为数组[context,args]
             fired = true ;   //表示已经执行,用于表示队列里的回调已经执行过一次
             firingIndex = firingStart || 0;   //执行队列的下标,相当于普通循环的i,当$.Callbacks('memory')时,需设置firingIndex
             firingStart = 0;                  //重置队列起始值
             firingLength = list.length;       //保存队列的长度
             firing = true ;                    //标示正在执行中
             for  ( ; list && firingIndex < firingLength; firingIndex++ ) {  //
                 //遍历回调数组,执行每一个回调,当回调返回false且有传入stopOnFalse时,也就是$.Callbacks('stopOnFalse'),中止后面回调的执行. PS:这个循环每次都判断list,这个没有必要,可以提到外面判断一次即可
                 if  ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false  && options.stopOnFalse ) {
                     memory = false ; // To prevent further calls using add //当$.Callbacks('memory stopOnFalse')时,memory的作用将失效
                     break ;
                 }
             }
             firing = false //函数执行完后,将执行中的标示设为false
             if  ( list ) {
                 if  ( stack ) {  //执行完回调后,看一下stack是否有回调,有拿出来执行
                     if  ( stack.length ) {
                         fire( stack.shift() );
                     }
                 } else  if  ( memory ) {  //如果没有stack,证明传了once,这里的Callbacks会是这样:$.Callbacks('once memory')
                     list = [];
                 } else  {                //当是$.Callbacks('once')的时候
                     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;  //添加回调函数之前,先保存当前回调函数列表的长度,主要用于当Callbacks传入memory参数时
   
                     //这里用了一个立即执行的add函数来添加回调
                     //直接遍历传过来的arguments进行push
                     ( function  add( args ) {
                         jQuery.each( args, function ( _, arg ) {
                             var  type = jQuery.type( arg );
                             //如果所传参数为函数,则push
                             if  ( type === "function"  ) {
                                 if  ( !options.unique || !self.has( arg ) ) {  //当$.Callbacks('unique')时,保证列表里面不会出现重复的回调
                                     list.push( arg );
                                 }
                             } else  if  ( arg && arg.length && type !== "string"  ) {  //假如传过来的参数为数组或array-like,则继续调用添加,从这里可以看出add的传参可以有add(fn),add([fn1,fn2]),add(fn1,fn2)
                                 // Inspect recursively
                                 add( arg );
                             }
                         });
                     })( arguments );
   
                     // Do we need to add the callbacks to the
                     // current firing batch?
                     // 这里是我不解的地方,firing是标识回调数组正在执行中,也就是fire正在执行,那这里就重置回调数组的长度,但我不知道什么样的代码下这里会执行到
                     if  ( firing ) {
                         firingLength = list.length;
                     // With memory, if we're not firing then
                     // we should call right away
                     // 在fire方法里,只有options.memory为真时,memory才有值,所以,这里的memory保存的是上一次fire时的memory值,而这个的作用就是要立即执行新添加的回调,让新添加的回调也能输出之前fire时传的值。
                     //这里也是$.Callbacks('memory')这个参数作用的地方,有了这个参数,每次add也会执行一次memory
                     } else  if  ( memory ) {  
                         firingStart = start;   //上面保存的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 ;
             },
             // Control if a given callback is in the list
             has: function ( fn ) {
                 return  jQuery.inArray( fn, list ) > -1;
             },
             // Remove all callbacks from the list
             empty: function () {
                 list = [];
                 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 ) {
                 args = args || [];
                 args = [ context, args.slice ? args.slice() : args ];  //slice只用于字符串或数组,这里为什么要判断一下是不是字符串或数组呢,不明
                 if  ( list && ( !fired || stack ) ) {  //fired表示已经执行过,如果已经执行过了,就要看stack了,他为真才会继续执行
                     if  ( firing ) {  //firing表示执行中,如果是在执行中,则将其推入stack,stack在这里相当于一个缓存数组,用于当fire忙时暂存下回调,但我也是整不出一段代码让这里执行到,暂时不明白
                         stack.push( args );
                     } else  {
                         fire( args );  //执行回调
                     }
                 }
                 return  this ;
             },
             // Call all the callbacks with the given arguments
             // 直接调用fire,回调执行的上下文是self,而上面的fireWidth则可以通过传入context改变回调的执行上下文
             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;
};
资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率和项目质量的关键环节。Jenkins Pipeline作为一种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的一套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之一,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的转变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称和描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本和预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建和发布的流程。以下是一个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push package和Deploy/Rollback。每个阶段都可以根据实际需求进行配置和调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率和项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试和部署,从而提高项目的整体质量和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值