jQuery源码分析之jQuery工具方法

标签: JavaScript jQuery

分析jQuery工具方法,学习高手编写代码技巧。其中jQuery.typejQuery.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 //...... 省略代码
留言板
comments powered by Disqus