【jQuery源码】工具函数
1 //扩展工具函数 2 jQuery.extend({ 3 // Unique for each copy of jQuery on the page 4 expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), 5 6 // Assume jQuery is ready without the ready module 7 isReady: true, 8 9 error: function( msg ) { 10 throw new Error( msg ); 11 }, 12 13 noop: function() {}, 14 15 // See test/unit/core.js for details concerning isFunction. 16 // Since version 1.3, DOM methods and functions like alert 17 // aren't supported. They return false on IE (#2968). 18 isFunction: function( obj ) { 19 return jQuery.type(obj) === "function"; 20 }, 21 22 //首先判断是否支持ES5中的isArray 23 isArray: Array.isArray || function( obj ) { 24 return jQuery.type(obj) === "array"; 25 }, 26 27 //之前版本是obj && typeof obj === "object" && "setInterval" in obj; 28 isWindow: function( obj ) { 29 /* jshint eqeqeq: false */ 30 //window对象是一个包含自己的对象 window.window....window === window; 31 return obj != null && obj == obj.window; 32 }, 33 34 isNumeric: function( obj ) { 35 // parseFloat NaNs numeric-cast false positives (null|true|false|"") 36 // ...but misinterprets leading-number strings, particularly hex literals ("0x...") 37 // subtraction forces infinities to NaN 38 // adding 1 corrects loss of precision from parseFloat (#15100) 39 //当不是数字时,(obj - parseFloat( obj ) + 1)为NaN,所以>=0也就为false 40 return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; 41 }, 42 43 isEmptyObject: function( obj ) { 44 var name; 45 for ( name in obj ) { 46 return false; 47 } 48 return true; 49 }, 50 51 // 检查obj是否是一个纯粹的对象(通过"{}" 或 "new Object"创建的对象) 52 // console.info( $.isPlainObject( {} ) ); // true 53 // console.info( $.isPlainObject( '' ) ); // false 54 // console.info( $.isPlainObject( document.location ) ); // true 55 // console.info( $.isPlainObject( document ) ); // false 56 // console.info( $.isPlainObject( new Date() ) ); // false 57 // console.info( $.isPlainObject( ) ); // false 58 isPlainObject: function( obj ) { 59 var key; 60 61 // Must be an Object. 62 // Because of IE, we also have to check the presence of the constructor property. 63 // Make sure that DOM nodes and window objects don't pass through, as well 64 // 必须是一个对象 65 // 因为在IE8中会抛出非法指针异常,必须检查constructor属性 66 // DOM节点和window对象,返回false 67 // obj不存在 或 非object类型 或 DOM节点 或 widnow对象,直接返回false 68 // 测试以下三中可能的情况: 69 // jQuery.type(obj) !== "object" 类型不是object,忽略 70 // obj.nodeType 认为DOM节点不是纯对象 71 // jQuery.isWindow( obj ) 认为window不是纯对象 72 if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { 73 return false; 74 } 75 76 try { 77 // Not own constructor property must be Object 78 // 测试constructor属性 79 // 具有构造函数constructor,却不是自身的属性(即通过prototype继承的) 80 if ( obj.constructor && 81 !hasOwn.call(obj, "constructor") && 82 !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { 83 return false; 84 } 85 } catch ( e ) { 86 // IE8,9 Will throw exceptions on certain host objects #9897 87 return false; 88 } 89 90 // Support: IE<9 91 // Handle iteration over inherited properties before own properties. 92 if ( support.ownLast ) { 93 for ( key in obj ) { 94 return hasOwn.call( obj, key ); 95 } 96 } 97 98 // Own properties are enumerated firstly, so to speed up, 99 // if last one is own, then all properties are own. 100 for ( key in obj ) {} 101 102 return key === undefined || hasOwn.call( obj, key ); 103 }, 104 105 type: function( obj ) { 106 if ( obj == null ) { 107 return obj + "";//转成字符串的一种方法 108 } 109 return typeof obj === "object" || typeof obj === "function" ? 110 class2type[ toString.call(obj) ] || "object" : 111 typeof obj; 112 }, 113 114 // Evaluates a script in a global context 115 // Workarounds based on findings by Jim Driscoll 116 // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context 117 globalEval: function( data ) { 118 if ( data && jQuery.trim( data ) ) { 119 // We use execScript on Internet Explorer 120 // We use an anonymous function so that context is window 121 // rather than jQuery in Firefox 122 ( window.execScript || function( data ) { 123 window[ "eval" ].call( window, data ); 124 } )( data ); 125 } 126 }, 127 128 // Convert dashed to camelCase; used by the css and data modules 129 // Microsoft forgot to hump their vendor prefix (#9572) 130 camelCase: function( string ) { 131 return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); 132 }, 133 134 //判断节点名称是否相同 135 nodeName: function( elem, name ) { 136 return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); 137 }, 138 139 // args is for internal usage only 140 //遍历对象或数组 141 each: function( obj, callback, args ) { 142 var value, 143 i = 0, 144 length = obj.length, 145 isArray = isArraylike( obj ); 146 147 //如果有参数 148 if ( args ) { 149 if ( isArray ) { 150 for ( ; i < length; i++ ) { 151 value = callback.apply( obj[ i ], args ); 152 153 if ( value === false ) { 154 break; 155 } 156 } 157 } else { 158 for ( i in obj ) { 159 value = callback.apply( obj[ i ], args ); 160 161 if ( value === false ) { 162 break; 163 } 164 } 165 } 166 167 // A special, fast, case for the most common use of each 168 //没有参数args则调用,则调用call,上下文设置为当前遍历到的对象,参数设置为key/index和value 169 } else { 170 if ( isArray ) { 171 for ( ; i < length; i++ ) { 172 value = callback.call( obj[ i ], i, obj[ i ] ); 173 174 if ( value === false ) { 175 break; 176 } 177 } 178 } else { 179 for ( i in obj ) { 180 value = callback.call( obj[ i ], i, obj[ i ] ); 181 182 if ( value === false ) { 183 break; 184 } 185 } 186 } 187 } 188 189 return obj; 190 }, 191 192 // Support: Android<4.1, IE<9 193 //利用正则去掉前后空格 194 trim: function( text ) { 195 return text == null ? 196 "" : 197 ( text + "" ).replace( rtrim, "" ); 198 }, 199 200 // results is for internal usage only 201 makeArray: function( arr, results ) { 202 var ret = results || []; 203 204 if ( arr != null ) { 205 if ( isArraylike( Object(arr) ) ) {//伪数组 206 jQuery.merge( ret, 207 typeof arr === "string" ? 208 [ arr ] : arr 209 ); 210 } else {//不是数组也不是伪数组 211 push.call( ret, arr ); 212 } 213 } 214 215 return ret; 216 }, 217 218 //在数组中搜索指定的值,并返回其索引值,i为查找的起始位置 219 inArray: function( elem, arr, i ) { 220 var len; 221 222 if ( arr ) { 223 if ( indexOf ) {// 是否有本地化的Array.prototype.indexOf 224 return indexOf.call( arr, elem, i ); 225 } 226 227 len = arr.length; 228 i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; 229 230 for ( ; i < len; i++ ) { 231 // Skip accessing in sparse arrays 232 //i in arr表示arr[i]存在 233 if ( i in arr && arr[ i ] === elem ) { 234 return i; 235 } 236 } 237 } 238 239 return -1; 240 }, 241 242 //将数组second合并到数组first中 243 merge: function( first, second ) { 244 var len = +second.length, 245 j = 0, 246 i = first.length; 247 248 while ( j < len ) { 249 first[ i++ ] = second[ j++ ]; 250 } 251 252 // Support: IE<9 253 // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) 254 //在IE9以前,类数组对象的length为NaN 255 if ( len !== len ) { 256 while ( second[j] !== undefined ) { 257 first[ i++ ] = second[ j++ ]; 258 } 259 } 260 261 first.length = i; 262 263 return first; 264 }, 265 266 // 过滤数组,返回新数组;callback返回true时保留;如果inv为true,callback返回false才会保留 267 grep: function( elems, callback, invert ) { 268 var callbackInverse, 269 matches = [], 270 i = 0, 271 length = elems.length, 272 callbackExpect = !invert; 273 274 // Go through the array, only saving the items 275 // that pass the validator function 276 //遍历数组,将回调函数返回值push到结果数组 277 for ( ; i < length; i++ ) { 278 callbackInverse = !callback( elems[ i ], i ); 279 if ( callbackInverse !== callbackExpect ) { 280 matches.push( elems[ i ] ); 281 } 282 } 283 284 return matches; 285 }, 286 287 // arg is for internal usage only 288 //将数组或对象elems的元素/属性,转化成新的数组 289 map: function( elems, callback, arg ) { 290 var value, 291 i = 0, 292 length = elems.length, 293 isArray = isArraylike( elems ), 294 ret = []; 295 296 // Go through the array, translating each of the items to their new values 297 if ( isArray ) {//类数组或数组 298 for ( ; i < length; i++ ) { 299 value = callback( elems[ i ], i, arg ); 300 301 if ( value != null ) { 302 ret.push( value ); 303 } 304 } 305 306 // Go through every key on the object, 307 } else {//对象 308 for ( i in elems ) { 309 value = callback( elems[ i ], i, arg ); 310 311 if ( value != null ) { 312 ret.push( value ); 313 } 314 } 315 } 316 317 // Flatten any nested arrays 318 // 使嵌套数组变平 319 // concat: 320 // 如果某一项为数组,那么添加其内容到末尾。 321 // 如果该项目不是数组,就将其作为单个的数组元素添加到数组的末尾。 322 return concat.apply( [], ret ); 323 }, 324 325 // A global GUID counter for objects 326 guid: 1, 327 328 // Bind a function to a context, optionally partially applying any 329 // arguments. 330 // 代理方法:为fn指定上下文(即this) 331 proxy: function( fn, context ) { 332 var args, proxy, tmp; 333 334 //如果context是字符串格式,fn为fn[context] 335 //上下文环境设为fn 336 if ( typeof context === "string" ) { 337 tmp = fn[ context ]; 338 context = fn; 339 fn = tmp; 340 } 341 342 // Quick check to determine if target is callable, in the spec 343 // this throws a TypeError, but we will just return undefined. 344 // 快速测试fn是否是可调用的(即函数) 345 // 但是这里仅返回undefined 346 if ( !jQuery.isFunction( fn ) ) { 347 return undefined; 348 } 349 350 // Simulated bind 351 args = slice.call( arguments, 2 );//从列表中去除前两个参数,即fn,context 352 proxy = function() { 353 return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); 354 }; 355 356 // Set the guid of unique handler to the same of original handler, so it can be removed 357 proxy.guid = fn.guid = fn.guid || jQuery.guid++; 358 359 return proxy; 360 }, 361 362 now: function() { 363 return +( new Date() ); 364 }, 365 366 // jQuery.support is not used in Core but other projects attach their 367 // properties to it so it needs to exist. 368 support: support 369 });