jQuery模块化的开端是jquery.js文件,在这个文件中详细的列出了jquery的模块。

一共有29个模块,其中有的是基础模块,像core别的模块都会依赖于它。
除了基础模块,别的模块都可以在自定义打包中决定是否需要。

打包之后的代码会被intro.jsoutro.js包围。形成如下代码的格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(function(window,factory){
//检测是否支持模块
if (typeof module === "object" && typeof module.exports === "object") {
//检查是否模块环境中是否开启了window对象
//如果支持模块化的话采用模块化实例化工厂
//否则就用默认方式
module.exports = function( w ) {
w = w || window;
if ( !w.document ) {
throw new Error("jQuery需要一个包含document的window对象");
}
return factory( w );
};
} else {
factory( window );
}
}(this,function(window){
// 框架主体部分
}));

core模块中有一些相关的初始化定义,比如jQuery的工厂方法:

1
2
3
4
5
jQuery = function( selector, context ) {
// jQuery.fn是原型
// 采用调用原型中init方法是实例化 在core/init实现
return new jQuery.fn.init( selector, context );
}

在通常中我们转化一个jQuery对象都是通过这个工厂方法来完成的。

core属性

core中定义了不少的属性,有必要知道其意义。

首先core依赖了不少的文件:

1
2
3
4
5
6
7
8
9
10
11
12
[
"./var/arr",
"./var/slice",
"./var/concat",
"./var/push",
"./var/indexOf",
"./var/class2type",
"./var/toString",
"./var/hasOwn",
"./var/trim",
"./var/support"
]

他们都是一些常用的方法或者变量。很多采用原生的实现。编译过后这些文件都会变成申明的变量或者函数。
类似于var arr = [];

缓存了一些常用的变量,减少多次访问不必要的解析时间。

分析如下:

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
var
// 使用document 缓存window.document
// 减少每次访问window.document时解析window的时间
// 不直接访问window.document会更安全(沙箱)
document = window.document,
// 用一个_jQuery来记录window.jQuery防止覆盖原始可能有的值
_jQuery = window.jQuery,
// 用一个_$来记录window.$防止覆盖原始可能有的值
_$ = window.$,
// 记录jquery版本
// 在grunt编译中"@VERSION"会被package.json文件中的version替换
version = "@VERSION",
// 定义了一个jQuery工厂方法来实例化对象
jQuery = function( selector, context ) {
// jQuery.fn是原型
// 采用调用原型中init方法是实例化 在core/init实现
return new jQuery.fn.init( selector, context );
},
// 匹配由'-ms-'开头的字符串
rmsPrefix = /^-ms-/,
//匹配由'-'分割的字符串
rdashAlpha = /-([\da-z])/gi,
// 把letter中字母转换为大写
fcamelCase = function( all, letter ) {
return letter.toUpperCase();
};

合并的申明变量方式比离散的申明变量方式更具有效率,jQuery果然是非常强大的东西。

jQuery原型扩展

在jQuery中采用jQuery.fn来申明原型扩展,原型中扩展了一些常用的方法与属性,下面是一些主要的说明:

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
jQuery.fn = jQuery.prototype = {
// 标明jQuery版本
jquery: version,
// javascript遗留问题 标明jQuery构造函数
constructor: jQuery,
// 用""来标明一开始的初始选择元素
selector: "",
// 默认jQuery对象的的长度为0
length: 0,
// 转化为数组输出
toArray: function() {
return slice.call( this );
},
// 根据数字返回node对象
// 不带参数会返回node的数组对象
get: function( num ) {
return num != null ?
// Return a 'clean' array
( num < 0 ? this[ num + this.length ] : this[ num ] ) :
// Return just the object
slice.call( this );
},
// 将一个DOM元素集合加入到jQuery栈。
// 返回新的元素集合
pushStack: function( elems ) {
// 合并两个个数组
var ret = jQuery.merge( this.constructor(), elems );
// 把老对象添加在prevObject属性
ret.prevObject = this;
ret.context = this.context;
// 返回行的对象
return ret;
},
// 集合中每个元素执行一次回调
// 通常中的$("div").each()
each: function( callback, args ) {
return jQuery.each( this, callback, args );
},
// 根据回调生成新的数组
map: function( callback ) {
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
},
// 切分数组
slice: function() {
return this.pushStack( slice.apply( this, arguments ) );
},
// 返回第一个元素
first: function() {
return this.eq( 0 );
},
// 返回最后一个元素
last: function() {
return this.eq( -1 );
},
// 返回第i个元素
// -1 是最后一个元素
eq: function( i ) {
var len = this.length,
j = +i + ( i < 0 ? len : 0 );
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
},
// 返回之前的jQuery对象
end: function() {
return this.prevObject || this.constructor(null);
},
// 常见的数组原生方法
push: push,
sort: arr.sort,
splice: arr.splice
};

jQuery.extend

在jQuery中大量的使用了jQuery.extend。jQuery.extend作为jQuery的扩展方法,在jQuery中扮演了极其重要的作用。

jQuery.extend 对jQuery本身的属性和方法进行了扩展。

jQuery.fn.extend 对jQuery.fn的属性和方法进行了扩展。

但是在调用的时候都是同一个方法:

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
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
// 根据第一个参数来判断是否使用深度拷贝
if ( typeof target === "boolean" ) {
deep = target;
// 把第二个参数记录到target
target = arguments[ i ] || {};
i++;
}
// 如果目标不是对象或函数,则初始化为空对象
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
// 如果只指定了一个参数,则使用jQuery自身作为目标对象
if ( i === length ) {
target = this;
i--;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// 如果对象中包含了数组或者其他对象,则使用递归进行拷贝
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
//处理数组
if ( copyIsArray ) {
copyIsArray = false;
// 如果目标对象不存在该数组,则创建一个空数组
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// 不改变原始对象,只做拷贝
target[ name ] = jQuery.extend( deep, clone, copy );
// 不拷贝 undefined 值
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// 返回修改后的对象
return target;
};

jQuery基础方法和属性

jQuery工厂中不仅有常见的方法,还有常用的属性。

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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/**
* 直接扩展在原jQuery中
*/
jQuery.extend({
// 在页面区别不同的jQuery实例存储的数据,
// 使用前缀jQuery+版本号+随机数作为Key
expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
// 标识jQuery对象 可用 不使用ready模块
isReady: true,
// 产生一个异常
error: function( msg ) {
throw new Error( msg );
},
// 一个空函数
noop: function() {},
// 解决命名冲突
noConflict: function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
}
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
},
// 检测是否是方法函数
// jQuery 1.3以后,浏览器提供的函数比如alert()还有 DOM 元素的方法比如 getAttribute() 将不认为是函数
// 因为各种浏览器typeof 返回的字符串不一致
// 例如:在IE上返回false
isFunction: function( obj ) {
return jQuery.type(obj) === "function";
},
// 原生的isArray方法
isArray: Array.isArray,
// 判断是否是window对象
isWindow: function( obj ) {
return obj != null && obj === obj.window;
},
// 判读是否是数字
isNumeric: function( obj ) {
// parseFloat NaNs numeric-cast false positives (null|true|false|"")
// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
// subtraction forces infinities to NaN
return obj - parseFloat( obj ) >= 0;
},
// 判断对象否为纯粹的对象字面量
isPlainObject: function( obj ) {
// Not plain objects:
// - Any object or value whose internal [[Class]] property is not "[object Object]"
// - DOM nodes
// - window
if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
// Support: Firefox <20
// The try/catch suppresses exceptions thrown when attempting to access
// the "constructor" property of certain host objects, ie. |window.location|
// https://bugzilla.mozilla.org/show_bug.cgi?id=814622
try {
if ( obj.constructor &&
!hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
return false;
}
} catch ( e ) {
return false;
}
// If the function hasn't returned already, we're confident that
// |obj| is a plain object, created by {} or constructed with new Object
return true;
},
// 判断是否为空对象
isEmptyObject: function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
},
// 判断对象类型
type: function( obj ) {
if ( obj == null ) {
return obj + "";
}
// Support: Android < 4.0, iOS < 6 (functionish RegExp)
return typeof obj === "object" || typeof obj === "function" ?
class2type[ toString.call(obj) ] || "object" :
typeof obj;
},
// 在全局域执行代码
globalEval: function( code ) {
var script,
indirect = eval;
code = jQuery.trim( code );
if ( code ) {
// If the code includes a valid, prologue position
// strict mode pragma, execute code by injecting a
// script tag into the document.
if ( code.indexOf("use strict") === 1 ) {
script = document.createElement("script");
script.text = code;
document.head.appendChild( script ).parentNode.removeChild( script );
} else {
// Otherwise, avoid the DOM node creation, insertion
// and removal by using an indirect global eval
indirect( code );
}
}
},
// Convert dashed to camelCase; used by the css and data modules
// Microsoft 会忽略驼峰的前缀 (#9572)
camelCase: function( string ) {
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
},
// 获得元素的节点名称
nodeName: function( elem, name ) {
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
},
// 每个元素回调一次
each: function( obj, callback, args ) {
var value,
i = 0,
length = obj.length,
isArray = isArraylike( obj );
if ( args ) {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
}
// A special, fast, case for the most common use of each
} else {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
}
}
return obj;
},
// 去掉前后空格
trim: function( text ) {
return text == null ? "" : trim.call( text );
},
// 转换一个类似数组的对象成为真正的JavaScript数组
makeArray: function( arr, results ) {
var ret = results || [];
if ( arr != null ) {
if ( isArraylike( Object(arr) ) ) {
jQuery.merge( ret,
typeof arr === "string" ?
[ arr ] : arr
);
} else {
push.call( ret, arr );
}
}
return ret;
},
// 在数组中查找指定值并返回它的索引(如果没有找到,则返回-1)
inArray: function( elem, arr, i ) {
return arr == null ? -1 : indexOf.call( arr, elem, i );
},
// 合并两个数组
merge: function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
for ( ; j < len; j++ ) {
first[ i++ ] = second[ j ];
}
first.length = i;
return first;
},
// 查找满足过滤函数的数组元素
// 原始数组不受影响
grep: function( elems, callback, invert ) {
var callbackInverse,
matches = [],
i = 0,
length = elems.length,
callbackExpect = !invert;
// 查找数组 保存满足匹配的元素
for ( ; i < length; i++ ) {
callbackInverse = !callback( elems[ i ], i );
if ( callbackInverse !== callbackExpect ) {
matches.push( elems[ i ] );
}
}
return matches;
},
// 将一个数组中的所有元素转换到另一个数组中
map: function( elems, callback, arg ) {
var value,
i = 0,
length = elems.length,
isArray = isArraylike( elems ),
ret = [];
// 把数组中的每一个值转化为新值
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
// 检测新的key值
} else {
for ( i in elems ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
}
// 合并数组
return concat.apply( [], ret );
},
// 全局 GUID 用来记录对象个数
guid: 1,
// 接受一个函数,然后返回一个新函数,并且这个新函数始终保持了特定的上下文语境
proxy: function( fn, context ) {
var tmp, args, proxy;
if ( typeof context === "string" ) {
tmp = fn[ context ];
context = fn;
fn = tmp;
}
// 快速检查,以确定目标是否可调用,
// 在规范这将引发一个TypeError,但我们将只返回undefined。
if ( !jQuery.isFunction( fn ) ) {
return undefined;
}
// 模拟绑定
args = slice.call( arguments, 2 );
proxy = function() {
return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
};
// 为相同的原处理设置唯一的guid 保证其可以删除。
proxy.guid = fn.guid = fn.guid || jQuery.guid++;
return proxy;
},
now: Date.now,
// 一个空对象
// core模块没意思使用 但是别的地方会用到
support: support
});

总结

core 做为jQuery框架的基础结构模块,在其中申明实现了jQuery中很多常用的方法与属性。

提供了一个实例化jQuery对象的工厂方法,具体实现交给了init模块。