分析jQuery工具方法,学习高手编写代码技巧。其中jQuery.type、jQuery.map实现独特。
1 //...... 省略代码
2 var //...... 省略代码
3 trimLeft = /^\s+/,
4 trimRight = /\s+$/,
5 //...... 省略代码
6 //_jQuery/_$分别保存全局变量jQuery/$,以防命名冲突
7 _jQuery = window.jQuery,
8 _$ = window.$,
9 //...... 省略代码
10 //缓存常用对象的方法为局部变量,提高访问效率
11 toString = Object.prototype.toString,
12 hasOwn = Object.prototype.hasOwnProperty,
13 push = Array.prototype.push,
14 slice = Array.prototype.slice,
15 trim = String.prototype.trim,
16 indexOf = Array.prototype.indexOf,
17 class2type = {};
18 //...... 省略代码
19 jQuery.extend({
20 //解决命名冲突,将原先_$/_jQuery重新赋值给全局变量$/jQuery,并返回jQuery的本地实现。
21 //调用该方法后,外部不能通过$/jQuery名称访问jQuery的本地实现,只能用该函数返回值进行访问。
22 //此方法的妙处在于命名冲突时,可将返回值赋值给外部的变量,再继续访问jQuery的本地实现。
23 noConflict: function( deep ) {
24 if ( window.$ === jQuery ) {
25 window.$ = _$;
26 }
27 if ( deep && window.jQuery === jQuery ) {
28 window.jQuery = _jQuery;
29 }
30 return jQuery;
31 },
32 //获取obj的数据类型
33 //检查对象数据类型可用typeof操作符,但typeof得到的类型值不靠谱,比如Date/RegExp对象的结果为object,而不是date/regExp。
34 //这里是调用toString(Object.prototype.toString)获取对象的类型信息,这样较为妥当。也是建议的写法。
35 //Date对象,调用toString方法后返回字符串[object Date];
36 //RegExp对象,调用toString方法后返回字符串[object RegExp]。
37 //变量class2type存储的key值为toString返回的字符串值(比如[object Function]),value值为类型名称(比如function)。
38 //可通过class2type[toString.call(obj)]获取对象obj的类型。
39 type: function( obj ) {
40 return obj == null ? String( obj ) : class2type[ toString.call(obj) ] || "object";
41 },
42 isFunction: function( obj ) {
43 return jQuery.type(obj) === "function";
44 },
45 //检测浏览器是否已实现Array.isArray,如是则无需再次实现。
46 isArray: Array.isArray || function( obj ) {
47 return jQuery.type(obj) === "array";
48 },
49 isWindow: function( obj ) {
50 return obj && typeof obj === "object" && "setInterval" in obj;
51 },
52 isNumeric: function( obj ) {
53 return !isNaN( parseFloat(obj) ) && isFinite( obj );
54 },
55 //判断是否普通对象。以下为普通对象
56 //var obj = new Object();obj.XXX = XXXX;
57 //var obj = {XXX:XXXX};
58 isPlainObject: function( obj ) {
59 //非普通对象
60 //值为''/0/false/null/undefined
61 //非Object类型数据(RegExp,Date,Array,Function对象不属于Object类型的哦)
62 //DOM对象(obj.nodeType来判断是否为DOM对象比较勉强,如果普通对象刚好有nodeType属性,那就杯具)
63 //window对象
64 if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
65 return false;
66 }
67 try {
68 //自定义的数据类型生成的对象为非普通对象
69 //所谓自定义数据类型即是构造函数的原型属性的值非指向Object.prototype
70 if ( obj.constructor && !hasOwn.call(obj, "constructor")
71 && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
72 return false;
73 }
74 } catch ( e ) {
75 //某些浏览器的内置对象调用hasOwn方法可能抛异常,做非普通对象处理
76 return false;
77 }
78 //for...in...会检索obj的原型链。该语句执行完后,
79 //若key=undefined表示obj为空对象(obj={}或obj=new Object()),则obj必是普通对象;
80 //若key值为仍属于obj的本地属性(ownProperty)而不是obj原型对象的属性,则obj必是普通对象。
81 var key;
82 for ( key in obj ) {}
83 return key === undefined || hasOwn.call( obj, key );
84 },
85 //object对象遍历执行callback
86 each: function( object, callback, args ) {
87 var name, i = 0,
88 //有length属性则将object当做数组处理(Arugments/Array等,String对象也有length属性)
89 length = object.length,
90 //无length属性或为函数类型对象则将object当做对象处理
91 isObj = length === undefined || jQuery.isFunction( object );
92 if ( args ) {
93 if ( isObj ) {
94 //object为对象时则使用for...in...遍历各属性
95 for ( name in object ) {
96 if ( callback.apply(object[ name ], args)===false ) {
97 break;
98 }
99 }
100 } else {
101 //object为数组时则使用for遍历各元素
102 for ( ; i < length; ) {
103 if ( callback.apply(object[ i++ ], args)===false ) {
104 break;
105 }
106 }
107 }
108 } else {
109 if ( isObj ) {
110 for ( name in object ) {
111 //未传args时,则将object对象的属性及属性值作为参数传入
112 if ( callback.call(object[name], name, object[ name ])===false ) {
113 break;
114 }
115 }
116 } else {
117 for ( ; i < length; ) {
118 //未传args时,则将object数组的下标及元素值作为参数传入
119 if ( callback.call(object[ i ], i, object[ i++ ])===false ) {
120 break;
121 }
122 }
123 }
124 }
125 return object;
126 },
127 //去除开始结尾处空格
128 //检测浏览器是否已实现trim函数,如是则无需再重新实现
129 trim: trim ?
130 function( text ) {
131 return text == null ?"" :trim.call( text );
132 } :
133 function( text ) {
134 return text==null?"":text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
135 },
136 makeArray: function( array, results ) {
137 var ret = results || [];
138 if ( array != null ) {
139 var type = jQuery.type( array );
140 if ( array.length == null || type === "string"
141 || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
142 //非数组Array或非类数组(Arguments)时,则放入数组
143 push.call( ret, array );
144 } else {
145 //数组Array或类数组(Arguments)时,则合并数组
146 jQuery.merge( ret, array );
147 }
148 }
149 return ret;
150 },
151 //获取数组array元素elem的下标,i为检索的开始下标值,默认为零。
152 inArray: function( elem, array, i ) {
153 var len;
154 if ( array ) {
155 //检测indexOf方法浏览器是否已实现,如是则直接调用返回结果
156 if ( indexOf ) {
157 return indexOf.call( array, elem, i );
158 }
159 len = array.length;
160 i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
161 for ( ; i < len; i++ ) {
162 if ( i in array && array[ i ] === elem ) {
163 return i;
164 }
165 }
166 }
167 return -1;
168 },
169 //合并数据
170 merge: function( first, second ) {
171 var i = first.length,
172 j = 0;
173 if ( typeof second.length === "number" ) {
174 //合并数组或类数组的对象
175 for ( var l = second.length; j < l; j++ ) {
176 first[ i++ ] = second[ j ];
177 }
178 } else {
179 //合并属性名为数字的对象
180 while ( second[j] !== undefined ) {
181 first[ i++ ] = second[ j++ ];
182 }
183 }
184 first.length = i;
185 return first;
186 },
187 //收集通过过滤器函数callback的数组(类数组)元素值
188 grep: function( elems, callback, inv ) {
189 var ret = [], retVal;
190 //!!的作用是将inv转化为boolean类型数据,等同于inv=Boolean(inv);
191 //inv=''/0/undefined/null/false,则!!inv=false;inv=其他值,则!!inv=true;
192 inv = !!inv;
193 for ( var i = 0, length = elems.length; i < length; i++ ) {
194 retVal = !!callback( elems[ i ], i );
195 if ( inv !== retVal ) {
196 ret.push( elems[ i ] );
197 }
198 }
199 return ret;
200 },
201 //数组(类数组),对象遍历执行callback方法取得新数据后返回
202 map: function( elems, callback, arg ) {
203 var value, key, ret = [],
204 i = 0,length = elems.length,
205 //判断elems是否为数组(Array)对象或类数组(如Arguments)对象
206 isArray = elems instanceof jQuery
207 || length !== undefined
208 && typeof length === "number"
209 && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] )
210 || length === 0 || jQuery.isArray( elems ) ) ;
211 if ( isArray ) {
212 for ( ; i < length; i++ ) {
213 value = callback( elems[ i ], i, arg );
214 //若value=null/undefined时value!=null为false
215 if ( value != null ) {
216 //这句技巧性太强,完全可以用ret.push(value)替代
217 ret[ ret.length ] = value;
218 }
219 }
220 } else {
221 for ( key in elems ) {
222 value = callback( elems[ key ], key, arg );
223 //若value=null/undefined时value!=null为false
224 if ( value != null ) {
225 //这句技巧性太强,完全可以用ret.push(value)替代
226 ret[ ret.length ] = value;
227 }
228 }
229 }
230 //concat方法的作用是将数组中内嵌数组元素展开,即是将多维数组转化为一维数组
231 //[].concat.apply([],[1,[2,[3,4]],5,6,7])执行结果为一维数组[1,2,3,4,5,6,7]
232 return ret.concat.apply( [], ret );
233 }
234 //...... 省略代码
235 });
236 //初始化class2type变量。该变量存储对象的toString方法返回的字符串(比如[object Object])
237 //与该对象类型名称(比如object)的映射关系。供jQuery.type方法使用。
238 jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
239 class2type[ "[object " + name + "]" ] = name.toLowerCase();
240 });
241 //...... 省略代码