- 
        - 苏州网站建设带你一起玩转你不知道的jQuery
 苏州网站建设 苏州网站制作 苏州网页设计
- 方法 jQuery.extend() 和 jQuery.fn.extend() 执行的关键步骤如下所示: 1)修正参数 deep、target、源对象的起始下标。 2)逐个遍历源对象: - a. 遍历源对象的属性。 - b. 覆盖目标对象的同名属性;如果是深度合并,则先递归调用 jQuery.extend()。 下面分析方法 jQuery.extend() 和 jQuery.fn.extend() 的源码。 - 1. 定义 jQuery.extend() 和 jQuery.fn.extend() - 相关代码如下所示: - 324 jQuery.extend = jQuery.fn.extend = function() { - 第 324 行:因为参数的个数是不确定的,可以有任意多个,所以没有列出可接受的参数。 - 2. 定义局部变量 - 相关代码如下所示: - 325 var options, name, src, copy, copyIsArray, - 326 clone, target = arguments[0] || {}, - 327 i = 1, - 328 length = arguments.length, - 329 deep = false; - 第 325 ~ 329 行:定义一组局部变量,它们的含义和用途如下:  变量 options:指向某个源对象。
 变量 name:表示某个源对象的某个属性名。
 变量 src:表示目标对象的某个属性的原始值。
 变量 copy:表示某个源对象的某个属性的值。 
 变量 copyIsArray:指示变量 copy 是否是数组。 
 变量 clone:表示深度复制时原始值的修正值。 
 变量 target:指向目标对象。
 变量 i:表示源对象的起始下标。
 变量 length:表示参数的个数,用于修正变量 target。 
 变量 deep:指示是否执行深度复制,默认为 false。
 - 3. 修正目标对象 target、源对象起始下标 i 
 相关代码如下所示:- 331 // Handle a deep copy situation - 332 if ( typeof target === "boolean" ) { - 333 deep = target; - 334 target = arguments[1] || {}; - 335 // skip the boolean and the target - 336 i = 2; - 337 } - 338 // Handle case when target is a string or something (possible in deep copy) - 339 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - 340 target = {}; - 341 // extend jQuery itself if only one argument is passed - 342 if ( length === i ) { - 343 target = this; - 344 --i; - 345 } - 第 331~337 行:如果第一个参数是布尔值,则修正第一个参数为 deep,修正第二个参数 为目标对象 target,并且期望源对象从第三个元素开始。 - 变量 i 的初始值为 1,表示期望源对象从第 2 个元素开始;当第一个参数为布尔型时, 变量 i 变为 2,表示期望源对象从第 3 个元素开始。 - 第 339 ~ 342 行:如果目标对象 target 不是对象、不是函数,而是一个字符串或其他的 基本类型,则统一替换为空对象 {},因为在基本类型上设置非原生属性是无效的。例如: - var s = 'hi'; - s.test = 'hello'; - console.log( s.test ); // 打印 undefined - 第 344 ~ 348 行:变量 i 表示源对象开始的下标,变量 length 表示参数个数,如果二者相等,表示期望的源对象没有传入,则把 jQuery 或 jQuery.fn 作为目标对象,并且把源对象 的开始下标减一,从而使得传入的对象被当作源对象。变量 length 等于 i 可能有两种情况: -  extend( object ),只传入了一个参数。 -  extend( deep, object ),传入了两个参数,第一个参数是布尔值。 - 4. 逐个遍历源对象 - 相关代码如下所示: - 350 for ( ; i < length; i++ ) { - 第 350 行:循环变量 i 表示源对象开始的下标,很巧妙的用法。 - 
		(1)遍历源对象的属性 
 - 下面的代码用于遍历源对象的属性: - 351 // Only deal with non-null/undefined values - 352 if ( (options = arguments[ i ]) != null ) { - 353 // Extend the base object - 354 for ( name in options ) { - 第 352 行:arguments 是一个类似数组的对象,包含了传入的参数,可以通过整型下标 访问指定位置的参数。这行代码把获取源对象和对源对象的判断合并为一条语句,只有源对 象不是 null、undefined 时才会继续执行。 - 第 353 行:开始遍历单个源对象的属性。 - 
		(2)覆盖目标对象的同名属性 
 - 相关代码如下所示: - 355 src = target[ name ]; - 356 copy = options[ name ]; - 357 // Prevent never-ending loop - 358 if(target===copy){ - 359 continue; - 360 } - 361 // Recurse if we're merging plain objects or arrays - 362 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - 363 if ( copyIsArray ) { - 364 copyIsArray = false; - 365 clone = src && jQuery.isArray(src) ? src : []; - 366 }else{ - 367 clone = src && jQuery.isPlainObject(src) ? src : {}; } - 368 // Never move original objects, clone them - 369 target[ name ] = jQuery.extend( deep, clone, copy ); - 370 // Don't bring in undefined values } else if ( copy !== undefined ) { - 371 target[ name ] = copy; - 第 355 ~ 361 行:变量 src 是原始值,变量 copy 是复制值。如果复制值 copy 与目标对 象 target 相等,为了避免深度遍历时死循环,因此不会覆盖目标对象的同名属性。如果注释 掉第 360 行,下面的代码会抛出堆栈溢出异常: - var o = {}; - o.n1 = o; - $.extend( true, o, { n2: o } ); - // 抛出异常: - // Uncaught RangeError: Maximum call stack size exceeded - 注意,判断 target === copy 时使用的是“ ===”,强制不做类型转换;如果使用“ ==”, 则可能因自动类型转换而导致错误 。 - 第 364 ~ 374 行:如果是深度合并,且复制值 copy 是普通 JavaScript 对象或数组,则递 归合并。 - 第 365 ~ 371 行:复制值 copy 是数组时,如果原始值 src 不是数组,则修正为空数组; 复制值 copy 是普通 JavaScript 对象时,如果原始值 src 不是普通 JavaScript 对象,则修正为 空对象 {}。把原始值 src 或修正后的值赋值给原始值副本 clone。 - 通过调用方法 jQuery.isPlainObject( copy ) 判断复制值 copy 是否是“纯粹”的 JavaScript 对象,只有通过对象直接量 {} 或 new Object() 创建的对象,才会返回 true。例如: - jQuery.isPlainObject( { hello: 'world' } ); // true - jQuery.isPlainObject( new Object() ); // true - jQuery.isPlainObject( new Object(1) ); // true - .selector、.jquery、.length、.size()- 属性 selector 用于记录 jQuery 查找和过滤 DOM 元素时的选择器表达式,但不一定是可执行的选择器表达式,该属性更多的是为了方便调试。下面是一些示例: - $('div').find('p').selector // "div p" - $('div p').selector // "div p" - $('div').first().selector // "div.slice(0,1)" - 属性 jquery 表示正在使用的 jQuery 版本号。 - 属性 .length 表示当前 jQuery 对象中元素的个数。 - 方法 .size() 返回当前 jQuery 对象中元素的个数。方法 .size() 在功能上等价于属性 .length, - 但应该优先使用属性 .length,因为它没有函数调用开销。 - 属性 .selector、.jquery、.length、.size() 的相关代码如下所示: - 209 // Start with an empty selector - 210 selector: "", - 212 // The current version of jQuery being used - 213 jquery: "1.7.1", - 215 // The default length of a jQuery object is 0 - 216 length: 0, - 218 // The number of elements contained in the matched element set - 219 size: function() { - 220 return this.length; - 221 }, - .toArray()、.get( [index] )- 1. .toArray() - 方法 .toArray() 将当前 jQuery 对象转换为真正的数组,转换后的数组包含了所有元素。 方法 .toArray() 的实现巧妙地借用了数组的方法 slice(),相关的代码如下所示: - 86 // Save a reference to some core methods - 87 toString = Object.prototype.toString, - 88 hasOwn = Object.prototype.hasOwnProperty, - 89 push = Array.prototype.push, - 90 slice = Array.prototype.slice, - 91 trim = String.prototype.trim, - 92 indexOf = Array.prototype.indexOf, - 223 toArray: function() { - 224 return slice.call( this, 0 ); - 225 } - 第 86 ~ 92 行: 连 同 slice() 一 起 声 明 的 还 有 toString()、hasOwn()、trim()、indexOf(), 这里通过声明对这些核心方法的引用,使得在 jQuery 代码中可以借用这些核心方法的功能, 执行时可通过方法 call() 和 apply() 指定方法执行的环境,即关键字 this 所引用的对象。这种“借鸡下蛋”的技巧非常值得借鉴。 - 数组方法 slice() 返回数组的一部分,语法如下 - array.slice(start, end) - // 参数 start 表示数组片段开始处的下标。如果是负数,它声明从数组末尾开始算起的位置 - // 参数end 表示数组片段结束处的后一个元素的下标。如果没有指定这个参数,切分的数组包含从 start - 开始到数组结束的所有元素。如果这个参数是负数,它声明的是从数组尾部开始算起的元素 - 2. .get( [index] )- 方法 .get( [index] ) 返回当前 jQuery 对象中指定位置的元素或包含了全部元素的数组。 如果没有传入参数,则调用 .toArray() 返回包含了所有元素的数组;如果指定了参数 index, 则返回一个单独的元素;参数 index 从 0 开始计算,并且支持负数,负数表示从元素集合末尾开始计算。相关代码如下所示: - 227 // Get the Nth element in the matched element set OR - 228 // Get the whole matched element set as a clean array - 229 get: function( num ) { - 230 return num == null ? - 231 // Return a 'clean' array - 232 this.toArray() : - 233 // Return just the object - 234 ( num < 0 ? this[ this.length + num ] : this[ num ] ); }, - 第 236 行:先判断 num 是否小于 0,如果小于 0,则用 length + num 重新计算下标,然 后使用数组访问操作符([])获取指定位置的元素,这是支持下标为负数的一个小技巧;如果num 大于等于 0,则直接返回指定位置的元素。 - .each( function(index, Element) )、jQuery.each( collection, callback (indexInArray, valueOfElement) )- 方法 .each() 遍历当前 jQuery 对象,并在每个元素上执行回调函数。每当回调函数执行时,会 传递当前循环次数作为参数,循环次数从 0 开始计数;更重要的是,回调函数是在当前元素为上 下文的语境中触发的,即关键字 this 总是指向当前元素;在回调函数中返回 false 可以终止遍历。 - 方法 .each() 内部通过简单的调用静态方法 jQuery.each() 实现,相关代码如下所示: - 267 // Execute a callback for every element in the matched set. - 268 // (You can seed the arguments with an array of args, but this is - 269 // only used internally.) - 270 each: function( callback, args ) { - 271 return jQuery.each( this, callback, args ); - 272 } - 2. jQuery.each( collection, callback(indexInArray, valueOfElement) )- 静态方法 jQuery.each() 是一个通用的遍历迭代方法,用于无缝地遍历对象和数组。对于 数组和含有 length 属性的类数组对象(如函数参数对象 arguments),该方法通过下标遍历, 从 0 到 length-1 ;对于其他对象则通过属性名遍历(for-in)。在遍历过程中,如果回调函数 返回 false,则结束遍历。 - 定义方法 jQuery.each( object, callback, args ),它接受 3 个参数。 - 
		 参数 object:待遍历的对象或数组。 
- 
		 参数 callback:回调函数,会在数组的每个元素或对象的每个属性上执行。 
- 
		 参数 args:传给回调函数 callback 的参数数组,可选。如果没有传入参数 args,则执行回调函数时会传入两个参数(下标或属性名,对应的元素或属性值)如果传入了参数 args,则只把该参数传给回调函数。 
 
 
- 
		
- 上一篇: 到底怎么回事?为什么电脑存储盘没有A、B盘,而是从C盘开始划分的?
- 下一篇: 跳一跳只是个开胃菜,微信小程序是要准备开足马力爆发吗?
 
- 苏州网站建设带你一起玩转你不知道的jQuery
 

 


