From 0daa4d88dc27294b2e8f2b90d873b1d866ddc664 Mon Sep 17 00:00:00 2001 From: wangjiaoyan <715068310@qq.com> Date: Wed, 18 Dec 2024 19:28:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/static/script/jquery-1.7.2.js | 2197 ++++++++++++++++------------- 1 file changed, 1230 insertions(+), 967 deletions(-) diff --git a/web/static/script/jquery-1.7.2.js b/web/static/script/jquery-1.7.2.js index 391f195..714bbf4 100644 --- a/web/static/script/jquery-1.7.2.js +++ b/web/static/script/jquery-1.7.2.js @@ -15,987 +15,1225 @@ */ (function( window, undefined ) { -// Use the correct document accordingly with window argument (sandbox) -var document = window.document, - navigator = window.navigator, - location = window.location; -var jQuery = (function() { - -// Define a local copy of jQuery -var jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); - }, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, +// 使用与窗口参数相对应的正确文档(沙箱环境) + var document = window.document, + // 获取当前窗口的文档对象 + navigator = window.navigator, + // 获取浏览器的导航信息 + location = window.location; + // 获取当前文档的地址信息 + var jQuery = (function() { + +// 定义一个局部的 jQuery 函数 + var jQuery = function( selector, context ) { + // jQuery 对象实际上是经过增强的 init 构造函数 + return new jQuery.fn.init( selector, context, rootjQuery ); + // 返回初始化后的 jQuery 实例 + }, - // Map over the $ in case of overwrite - _$ = window.$, + // 在窗口对象中保存 jQuery 的引用,以防被覆盖 + _jQuery = window.jQuery, + + // 在窗口对象中保存 $ 的引用,以防被覆盖 + _$ = window.$, + + // 指向根 jQuery(document) 的中央引用 + rootjQuery, + + // 简单检查 HTML 字符串或 ID 字符串的正则表达式 + // 优先考虑 #id 以避免通过 location.hash 引发 XSS (安全漏洞 #9521) + quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // 检查字符串是否包含非空白字符的正则表达式 + rnotwhite = /\S/, + + // 用于修剪左右两端空白字符的正则表达式 + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // 匹配独立标签的正则表达式 + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON 验证相关的正则表达式 + rvalidchars = /^[\],:{}\s]*$/, + // 验证 JSON 字符串中的字符 + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + // 匹配有效的 JSON 转义字符 + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + // 匹配 JSON 中的有效令牌 + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + // 匹配 JSON 中的括号 + + + // 用户代理相关的正则表达式,用于检测浏览器类型 + rwebkit = /(webkit)[ \/]([\w.]+)/, + // 检测 WebKit 浏览器 + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + // 检测 Opera 浏览器 + rmsie = /(msie) ([\w.]+)/, + // 检测 Internet Explorer 浏览器 + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + // 检测 Mozilla 浏览器 + + // 匹配带有短横线的字符串以进行驼峰化 + rdashAlpha = /-([a-z]|[0-9])/ig, + // 匹配短横线后跟字母或数字的模式 + rmsPrefix = /^-ms-/, + // 匹配以 -ms- 开头的前缀 + + // 被 jQuery.camelCase 使用的回调函数,用于字符串替换 + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + // 将匹配到的字母转换为大写 + }, - // A central reference to the root jQuery(document) - rootjQuery, + // 保存用户代理字符串,以供 jQuery.browser 使用 + userAgent = navigator.userAgent, + + // 用于匹配浏览器的引擎和版本 + browserMatch, + + // 用于 DOM 准备就绪的延迟对象 + readyList, + + // 准备就绪事件处理程序 + DOMContentLoaded, + + // 保存对一些核心方法的引用 + toString = Object.prototype.toString, + // 获取对象的字符串表示 + hasOwn = Object.prototype.hasOwnProperty, + // 检查对象是否有指定的属性 + push = Array.prototype.push, + // 数组的 push 方法 + slice = Array.prototype.slice, + // 数组的 slice 方法 + trim = String.prototype.trim, + // 字符串的 trim 方法,去除空白字符 + indexOf = Array.prototype.indexOf, + // 数组的 indexOf 方法,用于查找元素 + + // [[Class]] -> 类型对应关系的对象 + class2type = {}; + // 用于存储不同数据类型的映射关系 + +// jQuery.fn 和 jQuery.prototype 定义了 jQuery 的原型 + jQuery.fn = jQuery.prototype = { + constructor: jQuery, + // 构造函数指向 jQuery + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + // 定义局部变量 + + // 处理空选择器 $(""), $(null), 或 $(undefined) + if ( !selector ) { + return this; + // 返回当前对象 + } - // A simple way to check for HTML strings or ID strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + // 处理 $(DOMElement),当传入的是一个 DOM 元素时 + if ( selector.nodeType ) { + this.context = this[0] = selector; + // 将上下文和第一个元素设置为该元素 + this.length = 1; + // 设置长度为 1 + return this; + // 返回当前对象 + } - // Check if a string has a non-whitespace character in it - rnotwhite = /\S/, + // body 元素只存在一次,优化查找过程 + if ( selector === "body" && !context && document.body ) { + this.context = document; + // 设置上下文为文档 + this[0] = document.body; + // 将第一个元素设置为 body 元素 + this.selector = selector; + // 保存选择器字符串 + this.length = 1; + // 设置长度为 1 + return this; + // 返回当前对象 + } - // Used for trimming whitespace - trimLeft = /^\s+/, - trimRight = /\s+$/, + // 处理 HTML 字符串 + if ( typeof selector === "string" ) { + // 判断我们是处理 HTML 字符串还是 ID + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // 假设以 <> 开头和结尾的字符串是 HTML,因此跳过正则检查 + match = [ null, selector, null ]; + // 创建一个匹配数组 - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + } else { + match = quickExpr.exec( selector ); + // 使用正则表达式检查选择器 + } - // JSON RegExp - rvalidchars = /^[\],:{}\s]*$/, - rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, - rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + // 验证匹配,并且在指定 ID 时未提供上下文 + if ( match && (match[1] || !context) ) { + + // 处理: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + // 如果上下文是 jQuery 对象,则取其第一个元素 + doc = ( context ? context.ownerDocument || context : document ); + // 获取文档对象 + + // 如果传入的是单个字符串且它是单个标签 + // 则直接使用 createElement 跳过剩余处理 + ret = rsingleTag.exec( selector ); + // 检查是否为单一标签并执行相应操作 + + if ( ret ) { + // 如果 ret 存在,表示我们匹配到了一个 HTML 标签 + if ( jQuery.isPlainObject( context ) ) { + // 如果上下文是一个普通对象 + selector = [ document.createElement( ret[1] ) ]; + // 创建一个新的元素,并将其放入选择器数组中 + jQuery.fn.attr.call( selector, context, true ); + // 为新创建的元素设置属性 - // Useragent RegExp - rwebkit = /(webkit)[ \/]([\w.]+)/, - ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, - rmsie = /(msie) ([\w.]+)/, - rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + } else { + selector = [ doc.createElement( ret[1] ) ]; + // 否则,仅仅创建元素并放入选择器数组中 + } - // Matches dashed string for camelizing - rdashAlpha = /-([a-z]|[0-9])/ig, - rmsPrefix = /^-ms-/, + } else { + // 如果没有匹配到 HTML 标签 + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + // 构建一个文档片段 + selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; + // 从片段中获取子节点 + } - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return ( letter + "" ).toUpperCase(); - }, + return jQuery.merge( this, selector ); + // 将新创建的选择器与当前 jQuery 对象合并 - // Keep a UserAgent string for use with jQuery.browser - userAgent = navigator.userAgent, + // 处理: $("#id") + } else { + // 如果没有匹配到 HTML,检查是否为 ID 选择器 + elem = document.getElementById( match[2] ); + // 根据 ID 获取元素 + + // 检查 parentNode,以捕获当 Blackberry 4.6 返回 + // 不再在文档中的节点 #6963 + if ( elem && elem.parentNode ) { + // 如果元素存在且它有父节点 + // 处理 IE 和 Opera 可能会根据名称而非 ID 返回项的情况 + + if ( elem.id !== match[2] ) { + // 如果元素的 ID 与匹配的 ID 不相等 + return rootjQuery.find( selector ); + // 使用 jQuery 的查找方法进行搜索 + } - // For matching the engine and version of the browser - browserMatch, + // 否则,将元素直接注入到 jQuery 对象中 + this.length = 1; + // 设置长度为 1 + this[0] = elem; + // 将找到的元素放入 jQuery 对象中 - // The deferred used on DOM ready - readyList, + } - // The ready event handler - DOMContentLoaded, + this.context = document; + // 设置上下文为文档 + this.selector = selector; + // 保存选择器字符串 + return this; + // 返回当前 jQuery 对象 + } - // Save a reference to some core methods - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty, - push = Array.prototype.push, - slice = Array.prototype.slice, - trim = String.prototype.trim, - indexOf = Array.prototype.indexOf, + // 处理: $(expr, $(...)) + } else if ( !context || context.jquery ) { + // 如果没有上下文或上下文是一个 jQuery 对象,使用根 jQuery 对象查找选择器 + return ( context || rootjQuery ).find( selector ); - // [[Class]] -> type pairs - class2type = {}; + // 处理: $(expr, context) + // 这相当于: $(context).find(expr) + } else { + // 在指定的上下文中查找选择器 + return this.constructor( context ).find( selector ); + } -jQuery.fn = jQuery.prototype = { - constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem, ret, doc; + // 处理: $(function) + // 简化文档就绪的快捷方式 + } else if ( jQuery.isFunction( selector ) ) { + // 如果选择器是一个函数,调用根 jQuery 对象的 ready 方法 + return rootjQuery.ready( selector ); + } - // Handle $(""), $(null), or $(undefined) - if ( !selector ) { - return this; - } + // 如果选择器对象有 selector 属性,则保存上下文和选择器 + if ( selector.selector !== undefined ) { + this.selector = selector.selector;// 保存选择器字符串 + this.context = selector.context;// 保存上下文 + } - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } + // 将选择器转化为数组,并返回当前 jQuery 对象 + return jQuery.makeArray( selector, this ); + }, - // The body element only exists once, optimize finding it - if ( selector === "body" && !context && document.body ) { - this.context = document; - this[0] = document.body; - this.selector = selector; - this.length = 1; - return this; - } + // 选择器初始为空字符串 + selector: "", - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; + // 当前 jQuery 版本 + jquery: "1.7.2", - } else { - match = quickExpr.exec( selector ); - } + // jQuery 对象的默认长度为 0 + length: 0, - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { + // 获取匹配元素集合中的元素数量 + size: function() { + return this.length; + // 返回当前 jQuery 对象的长度 + }, - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - doc = ( context ? context.ownerDocument || context : document ); + toArray: function() { + // 将 jQuery 对象转换为原生数组并返回 + return slice.call( this, 0 ); + }, - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - ret = rsingleTag.exec( selector ); + // 获取匹配元素集合中的第 N 个元素,或返回整个匹配元素集合的干净数组 + get: function( num ) { + return num == null ? - if ( ret ) { - if ( jQuery.isPlainObject( context ) ) { - selector = [ document.createElement( ret[1] ) ]; - jQuery.fn.attr.call( selector, context, true ); + // 如果未提供参数,返回一个“干净”的数组 + this.toArray() : - } else { - selector = [ doc.createElement( ret[1] ) ]; - } + // 返回特定索引的对象,如果是负数则从后向前获取 + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, - } else { - ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); - selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; - } + // 接受一个元素数组并将其推入栈中(返回新的匹配元素集合) + pushStack: function( elems, name, selector ) { + // 创建一个新的 jQuery 匹配元素集合 + var ret = this.constructor(); - return jQuery.merge( this, selector ); + // 如果传入的元素是一个数组,则将其添加到新集合中 + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); - // HANDLE: $("#id") } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; + // 否则,将单个元素合并到新集合中 + jQuery.merge( ret, elems ); } - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); + // 将旧对象作为引用添加到栈中 + ret.prevObject = this; - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } + // 保存上下文 + ret.context = this.context; - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } + // 根据名称和选择器更新新的选择器字符串 + if ( name === "find" ) { + // 如果名称为 find,更新选择器为原选择器加上新选择器 + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + // 否则,格式化选择器为 "原选择器.方法名(选择器)" + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } - return jQuery.makeArray( selector, this ); - }, + // 返回新形成的元素集合 + return ret; + }, - // Start with an empty selector - selector: "", + // 为匹配集合中的每个元素执行回调函数。 + // (你可以用一个数组作为参数进行初始化,但这仅在内部使用。) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + // 调用 jQuery 的 each 方法,传入当前对象和回调 + }, - // The current version of jQuery being used - jquery: "1.7.2", + ready: function( fn ) { + // 绑定文档就绪事件的监听器 + jQuery.bindReady(); - // The default length of a jQuery object is 0 - length: 0, + // 添加回调函数到就绪列表中 + readyList.add( fn ); - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, + return this; + // 返回当前对象以便链式调用 + }, - toArray: function() { - return slice.call( this, 0 ); - }, + eq: function( i ) { + i = +i; + // 将输入转换为数字 + return i === -1 ? + // 如果输入是 -1,返回整个集合 + this.slice( i ) : + // 从集合的末尾返回元素 + this.slice( i, i + 1 ); + // 否则返回索引为 i 的单个元素 + }, - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? + first: function() { + return this.eq( 0 ); + // 返回集合中的第一个元素 + }, - // Return a 'clean' array - this.toArray() : + last: function() { + return this.eq( -1 ); + // 返回集合中的最后一个元素 + }, - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); - }, + slice: function() { + // 使用 slice 方法从当前集合中切片,并返回新的匹配集合 + return this.pushStack( + slice.apply( this, arguments ), + // 应用 slice 函数于当前集合 + "slice", + // 标识操作类型为 "slice" + slice.call(arguments).join(",") + // 组合切片参数并作为选择器 + ); + }, - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new jQuery matched element set - var ret = this.constructor(); + map: function( callback ) { + // 将回调应用于集合中的每个元素,并返回新的匹配集合 + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + // 在每个元素上调用回调 + })); + }, - if ( jQuery.isArray( elems ) ) { - push.apply( ret, elems ); + end: function() { + // 返回上一个 jQuery 对象或一个空的 jQuery 对象 + return this.prevObject || this.constructor(null); + }, - } else { - jQuery.merge( ret, elems ); - } + // 仅供内部使用。 + // 行为类似于数组的方法,而不是 jQuery 方法 + push: push, + // 将 push 方法赋给当前对象 + sort: [].sort, + // 将原生数组的 sort 方法赋给当前对象 + splice: [].splice + // 将原生数组的 splice 方法赋给当前对象 + }; - // Add the old object onto the stack (as a reference) - ret.prevObject = this; +// 将 init 函数的原型设置为 jQuery 的原型,以便后续实例化时使用 + jQuery.fn.init.prototype = jQuery.fn; - ret.context = this.context; +// 扩展 jQuery 和 jQuery.fn 的方法 + jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + // 声明变量用于存储 + target = arguments[0] || {}, + // 第一个参数作为目标对象,默认为空对象 + i = 1, + // 从第二个参数开始遍历 + length = arguments.length, + // 参数总数 + deep = false; + // 深拷贝标志,默认不深拷贝 - if ( name === "find" ) { - ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } + // 处理深拷贝情况 + if ( typeof target === "boolean" ) { + // 如果第一个参数是布尔值 + deep = target; + // 记录是否深拷贝 + target = arguments[1] || {}; + // 将第二个参数作为目标对象 - // Return the newly-formed element set - return ret; - }, + // 跳过布尔值和目标对象 + i = 2; + // 从第三个参数开始 + } - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, + // 处理目标为字符串或其他类型的情况(可能在深拷贝时发生) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + // 如果目标不是对象或函数,则将其重置为空对象 + } - ready: function( fn ) { - // Attach the listeners - jQuery.bindReady(); + // 如果只有一个参数,扩展 jQuery 本身 + if ( length === i ) { + target = this;// 将目标设置为当前对象 + --i;// 参数索引减一 + } - // Add the callback - readyList.add( fn ); + for ( ; i < length; i++ ) { + // 迭代参数列表,从第 i 个元素开始,直到 length - return this; - }, + // 只处理非空值(非 null 和 undefined) + if ( (options = arguments[ i ]) != null ) { + // 获取当前参数,并确保它不为 null 或 undefined - eq: function( i ) { - i = +i; - return i === -1 ? - this.slice( i ) : - this.slice( i, i + 1 ); - }, + // 扩展目标对象 + for ( name in options ) { + // 遍历 options 对象的每个属性 - first: function() { - return this.eq( 0 ); - }, + src = target[ name ]; // 获取目标对象中对应属性的值 + copy = options[ name ]; // 获取当前选项对象中对应属性的值 - last: function() { - return this.eq( -1 ); - }, + // 防止无限循环 + if ( target === copy ) { + // 如果源和目标是同一个对象,跳过 + continue; + } - slice: function() { - return this.pushStack( slice.apply( this, arguments ), - "slice", slice.call(arguments).join(",") ); - }, + // 如果正在合并普通对象或数组,则递归 + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + // 检查是否需要深拷贝以及当前属性是否为对象或数组 + if ( copyIsArray ) { + // 如果是数组 + copyIsArray = false; // 重置标志 + clone = src && jQuery.isArray(src) ? src : []; + // 如果目标对象中已有数组,则使用它,否则初始化一个空数组 - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, + } else { + // 如果是普通对象 + clone = src && jQuery.isPlainObject(src) ? src : {}; + // 如果目标对象中已有对象,则使用它,否则初始化一个空对象 + } - end: function() { - return this.prevObject || this.constructor(null); - }, + // 永远不要直接移动原始对象,进行克隆 + target[ name ] = jQuery.extend( deep, clone, copy ); + // 使用 jQuery.extend 方法深度合并克隆的对象与复制的对象 - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: [].sort, - splice: [].splice + // 不要引入 undefined 值 + } else if ( copy !== undefined ) { + // 如果 copy 不是 undefined,将其赋值给目标对象 + target[ name ] = copy; + } + } + } + } + // Return the modified object + return target; }; -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } + jQuery.extend({ + // noConflict 方法用于解除 jQuery 的冲突,使其与其他库共存 + noConflict: function (deep) { + // 如果全局变量 $ 是 jQuery,则将其恢复为之前的值 _$ + if (window.$ === jQuery) { + window.$ = _$; // 恢复原来的 $ 符号 + } - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } + // 如果深度冲突处理且全局变量 jQuery 是当前的 jQuery,则将其恢复为之前的值 _jQuery + if (deep && window.jQuery === jQuery) { + window.jQuery = _jQuery; // 恢复原来的 jQuery 符号 + } - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } + return jQuery; // 返回当前的 jQuery 对象 + }, - 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 ]; + // 用于跟踪 DOM 是否准备好可供使用,初始值为 false + isReady: false, - // Prevent never-ending loop - if ( target === copy ) { - continue; + // 用于计数等待多少个事件触发后 ready 事件会被触发,初始值为 1 + readyWait: 1, + + // holdReady 方法用于控制 ready 事件的触发 + holdReady: function (hold) { + if (hold) { + // 如果请求保持状态,则增加 readyWait 计数 + jQuery.readyWait++; + } else { + // 否则,触发 ready 事件并将参数设置为 true + jQuery.ready(true); } + }, - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; + // 处理 DOM 准备就绪的情况 + ready: function (wait) { + // 如果 wait 参数为 true 且 readyWait 计数减 1 后为 0,或者 wait 参数不是 true 且 isReady 为 false + if ((wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady)) { + // 确保文档的 body 存在,以防 IE 浏览器过于激进(参考 ticket #5443)。 + if (!document.body) { + // 如果 body 不存在,则延迟调用 jQuery.ready 方法 + return setTimeout(jQuery.ready, 1); + } - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; + // 标记 DOM 已经准备好 + jQuery.isReady = true; + + // 如果是常规的 DOM Ready 事件触发,计数减一,并在需要时等待 + if (wait !== true && --jQuery.readyWait > 0) { + return; // 如果还有其他等待的事件,则直接返回 } - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); + // 如果有绑定的函数,需要执行这些函数 + readyList.fireWith(document, [jQuery]); - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; + // 触发任何已绑定的 ready 事件 + if (jQuery.fn.trigger) { + // 触发 "ready" 事件并移除该事件的所有处理程序 + jQuery(document).trigger("ready").off("ready"); + } } - } - } - } + }, - // Return the modified object - return target; -}; + bindReady: function () { + // 如果 readyList 已经被定义,则直接返回,避免重复绑定 + if (readyList) { + return; + } -jQuery.extend({ - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } + // 创建一个新的回调列表,用于存储准备就绪的事件,设置为 "once memory" 选项 + readyList = jQuery.Callbacks("once memory"); - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } + // 捕获 $(document).ready() 在浏览器事件已发生后调用的情况 + if (document.readyState === "complete") { + // 以异步方式处理,以允许脚本有机会延迟 ready 状态 + return setTimeout(jQuery.ready, 1); + } - return jQuery; - }, + // Mozilla、Opera 和 Webkit Nightlies 当前支持这个事件 + if (document.addEventListener) { + // 使用方便的事件回调处理 DOMContentLoaded 事件 + document.addEventListener("DOMContentLoaded", DOMContentLoaded, false); - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, + // 为 window.onload 提供备用处理,这个处理会始终有效 + window.addEventListener("load", jQuery.ready, false); - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, + // 如果使用的是 IE 的事件模型 + } else if (document.attachEvent) { + // 确保在 onload 之前触发, + // 尽管可能稍晚,但对于 iframe 来说也很安全 + document.attachEvent("onreadystatechange", DOMContentLoaded); - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, + // 为 window.onload 提供备用处理,这个处理会始终有效 + window.attachEvent("onload", jQuery.ready); - // Handle when the DOM is ready - ready: function( wait ) { - // Either a released hold or an DOMready/load event and not yet ready - if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready, 1 ); - } + // 如果是在 IE 并且不是一个框架 + // 持续检查文档是否已准备好 + var toplevel = false; - // Remember that the DOM is ready - jQuery.isReady = true; + // 尝试判断当前窗口是否为顶层窗口 + try { + toplevel = window.frameElement == null; // 检查 frameElement 是否为空 + } catch (e) { + } - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } + // 如果文档支持 doScroll 并且是顶层窗口,就进行滚动检查 + if (document.documentElement.doScroll && toplevel) { + doScrollCheck(); // 调用 doScrollCheck 方法来检查文档准备状态 + } + } + }, - // If there are functions bound, to execute - readyList.fireWith( document, [ jQuery ] ); + // 参见 test/unit/core.js 文件以获取关于 isFunction 的详细信息。 +// 从版本 1.3 开始,不支持 DOM 方法和诸如 alert 的函数。 +// 在 IE 中,它们会返回 false (#2968)。 + isFunction: function (obj) { + // 检查给定对象的类型是否为 "function" + return jQuery.type(obj) === "function"; + }, - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger( "ready" ).off( "ready" ); - } - } - }, +// 判断是否为数组的函数。优先使用原生 Array.isArray 方法,如果不存在,则使用自定义实现。 + isArray: Array.isArray || function (obj) { + // 检查给定对象的类型是否为 "array" + return jQuery.type(obj) === "array"; + }, - bindReady: function() { - if ( readyList ) { - return; - } +// 判断给定对象是否为窗口对象的函数 + isWindow: function (obj) { + // 确保对象不为 null,并且确实是一个窗口对象 + return obj != null && obj == obj.window; + }, - readyList = jQuery.Callbacks( "once memory" ); +// 判断给定对象是否为数字的函数 + isNumeric: function (obj) { + // 检查将对象解析为浮点数后,是否不是 NaN 并且是有限数值 + return !isNaN(parseFloat(obj)) && isFinite(obj); + }, - // Catch cases where $(document).ready() is called after the - // browser event has already occurred. - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - return setTimeout( jQuery.ready, 1 ); - } +// 用于确定给定对象类型的通用函数 + type: function (obj) { + // 如果对象为 null,返回其字符串表示 + return obj == null ? + String(obj) : + // 根据对象的内部类型标签(通过 toString.call 获取)返回相应的类型名称, + // 如果找不到对应的类型,则返回 "object" + class2type[toString.call(obj)] || "object"; + }, - // Mozilla, Opera and webkit nightlies currently support this event - if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + isPlainObject: function (obj) { + // 必须是一个对象。 + // 由于 IE 的原因,我们还必须检查构造函数属性的存在性。 + // 确保 DOM 节点和窗口对象不会通过该检查。 + if (!obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow(obj)) { + // 如果 obj 为 null、不是对象类型、是 DOM 节点或是窗口对象,则返回 false + return false; + } - // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); + try { + // 检查 obj 的构造函数: + // 如果构造函数存在且不是本对象的属性,并且其原型上没有 isPrototypeOf 方法,则返回 false + if (obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { + return false; + } + } catch (e) { + // 在某些宿主对象上,IE8 和 IE9 会抛出异常,捕获这些异常并返回 false + return false; + } - // If IE event model is used - } else if ( document.attachEvent ) { - // ensure firing before onload, - // maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); + // 自有属性首先被枚举,因此为了提高速度, + // 如果最后一个 key 是自有属性,那么所有属性都是自有属性。 - // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); + var key; + for (key in obj) { + // 遍历 obj 的所有属性 + } - // If IE and not a frame - // continually check to see if the document is ready - var toplevel = false; + // 如果最后一个 key 是 undefined,表示 obj 没有可枚举的属性; + // 或者最后一个 key 是 obj 的自有属性,返回 true。 + return key === undefined || hasOwn.call(obj, key); + }, - try { - toplevel = window.frameElement == null; - } catch(e) {} + isEmptyObject: function (obj) { + // 遍历 obj 的所有属性 + for (var name in obj) { + // 如果 obj 中有任何属性,返回 false,表示对象不为空 + return false; + } + // 如果没有属性,则返回 true,表示对象为空 + return true; + }, - if ( document.documentElement.doScroll && toplevel ) { - doScrollCheck(); - } - } - }, + error: function (msg) { + // 抛出一个新的错误,错误信息为传入的 msg + throw new Error(msg); + }, - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, + parseJSON: function (data) { + // 首先检查传入的数据是否为字符串且非空 + if (typeof data !== "string" || !data) { + return null; // 如果不是字符串或为空,返回 null + } - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, + // 确保去掉首尾的空格(IE 无法处理) + data = jQuery.trim(data); - isWindow: function( obj ) { - return obj != null && obj == obj.window; - }, + // 尝试使用原生 JSON 解析器进行解析 + if (window.JSON && window.JSON.parse) { + return window.JSON.parse(data); // 若支持 JSON 解析,直接使用它进行解析 + } - isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, + // 确保传入的数据是有效的 JSON + // 逻辑借鉴自 http://json.org/json2.js + if (rvalidchars.test(data.replace(rvalidescape, "@") + .replace(rvalidtokens, "]") + .replace(rvalidbraces, ""))) { - type: function( obj ) { - return obj == null ? - String( obj ) : - class2type[ toString.call(obj) ] || "object"; - }, + // 如果数据有效,则利用 Function 构造函数执行并返回结果 + return (new Function("return " + data))(); + } - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } + // 如果数据无效,则调用 jQuery.error 抛出错误,包含具体的无效 JSON 数据 + jQuery.error("Invalid JSON: " + data); + }, - try { - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } + // 跨浏览器 XML 解析 + parseXML: function (data) { + // 首先检查传入的数据是否为字符串且非空 + if (typeof data !== "string" || !data) { + return null; // 如果不是字符串或为空,返回 null + } - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. + var xml, tmp; // 声明变量用于保存解析后的 XML 文档和临时对象 - var key; - for ( key in obj ) {} + try { + if (window.DOMParser) { // 标准浏览器支持 + tmp = new DOMParser(); // 创建一个 DOMParser 实例 + xml = tmp.parseFromString(data, "text/xml"); // 使用 DOMParser 解析字符串为 XML + } else { // IE 浏览器支持 + xml = new ActiveXObject("Microsoft.XMLDOM"); // 创建 ActiveXObject 实例 + xml.async = "false"; // 设置异步为 false,以同步解析 + xml.loadXML(data); // 加载 XML 字符串 + } + } catch (e) { + xml = undefined; // 捕获异常并将 xml 设置为 undefined + } - return key === undefined || hasOwn.call( obj, key ); - }, + // 检查解析结果是否有效 + if (!xml || !xml.documentElement || xml.getElementsByTagName("parsererror").length) { + // 如果没有根元素或者存在解析错误,则抛出错误 + jQuery.error("Invalid XML: " + data); + } - isEmptyObject: function( obj ) { - for ( var name in obj ) { - return false; - } - return true; - }, + return xml; // 返回解析后的 XML 文档 + }, - error: function( msg ) { - throw new Error( msg ); - }, + // 空函数,用于占位或回调 + noop: function () { + }, - parseJSON: function( data ) { - if ( typeof data !== "string" || !data ) { - return null; - } + // 在全局上下文中评估脚本 +// 根据 Jim Driscoll 的发现进行的解决方案 +// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function (data) { + // 检查数据是否存在且不为空(使用正则表达式 rnotwhite 测试) + if (data && rnotwhite.test(data)) { + // 在 Internet Explorer 中使用 execScript + // 使用匿名函数以确保上下文为 window,而不是 Firefox 中的 jQuery + (window.execScript || function (data) { + // 调用 window 上下文中的 eval 方法,以评估传入的数据 + window["eval"].call(window, data); + })(data); // 执行脚本 + } + }, - // Make sure leading/trailing whitespace is removed (IE can't handle it) - data = jQuery.trim( data ); +// 将带有连字符的字符串转换为驼峰命名法;在 css 和 data 模块中使用 +// 微软忘记了处理他们的供应商前缀 (#9572) + camelCase: function (string) { + // 用正则表达式将 'ms-' 前缀添加到字符串中,并将连字符后的字母转为大写 + return string.replace(rmsPrefix, "ms-").replace(rdashAlpha, fcamelCase); + }, - // Attempt to parse using the native JSON parser first - if ( window.JSON && window.JSON.parse ) { - return window.JSON.parse( data ); - } +// 检查元素的节点名称是否与给定名称匹配(不区分大小写) + nodeName: function (elem, name) { + // 如果元素存在,且其节点名称(大写形式)与给定名称(大写形式)匹配,则返回 true + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( rvalidchars.test( data.replace( rvalidescape, "@" ) - .replace( rvalidtokens, "]" ) - .replace( rvalidbraces, "")) ) { + // args 仅供内部使用 + each: function (object, callback, args) { + var name, i = 0, + length = object.length, // 获取对象的长度 + isObj = length === undefined || jQuery.isFunction(object); // 判断是否为对象或函数 + + // 如果提供了 args 参数 + if (args) { + // 判断是否为对象,如果是对象(或函数) + if (isObj) { + // 遍历对象的每个属性 + for (name in object) { + // 使用 apply 调用回调函数,传入当前对象和参数数组 + if (callback.apply(object[name], args) === false) { + break; // 如果回调返回 false,则停止遍历 + } + } + } else { + // 否则,遍历数组 + for (; i < length;) { + // 同样使用 apply 调用回调函数,传入当前元素和参数数组 + if (callback.apply(object[i++], args) === false) { + break; // 停止遍历条件同上 + } + } + } - return ( new Function( "return " + data ) )(); + // 处理没有 args 参数的特殊情况,通常是最常见的用法 + } else { + // 判断是否为对象 + if (isObj) { + // 遍历对象的每个属性 + for (name in object) { + // 使用 call 调用回调函数,传入当前属性名和属性值 + if (callback.call(object[name], name, object[name]) === false) { + break; // 停止遍历条件同上 + } + } + } else { + // 否则,遍历数组 + for (; i < length;) { + // 使用 call 调用回调函数,传入当前索引和元素 + if (callback.call(object[i], i, object[i++]) === false) { + break; // 停止遍历条件同上 + } + } + } + } - } - jQuery.error( "Invalid JSON: " + data ); - }, + return object; // 返回原始对象,方便链式调用 + }, - // Cross-browser xml parsing - parseXML: function( data ) { - if ( typeof data !== "string" || !data ) { - return null; - } - var xml, tmp; - try { - if ( window.DOMParser ) { // Standard - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } else { // IE - xml = new ActiveXObject( "Microsoft.XMLDOM" ); - xml.async = "false"; - xml.loadXML( data ); - } - } catch( e ) { - xml = undefined; - } - if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, + // 使用原生的 String.trim 函数(如果可用) + trim: trim ? + function (text) { + // 如果文本为 null,则返回空字符串 + return text == null ? + "" : + // 调用原生的 trim 方法来去除字符串两端的空白 + trim.call(text); + } : + + // 否则使用自定义的去空白功能 + function (text) { + // 如果文本为 null,则返回空字符串 + return text == null ? + "" : + // 将文本转换为字符串并使用正则表达式去除两端的空白 + text.toString().replace(trimLeft, "").replace(trimRight, ""); + }, - noop: function() {}, +// results 仅供内部使用 + makeArray: function (array, results) { + var ret = results || []; // 如果提供了 results,使用它;否则创建一个新的数组 - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && rnotwhite.test( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, + if (array != null) { // 检查 array 是否不为 null + // 获取 array 的类型 + var type = jQuery.type(array); - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, + // 如果 array 没有 length 属性,或者是字符串、函数、正则表达式或窗口对象 + if (array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow(array)) { + // 将 array 直接推入结果数组 + push.call(ret, array); + } else { + // 否则,合并 array 的元素到结果数组中 + jQuery.merge(ret, array); + } + } - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); - }, + return ret; // 返回处理后的结果数组 + }, - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, - length = object.length, - isObj = length === undefined || jQuery.isFunction( object ); + // 在数组中查找指定元素的位置 + inArray: function (elem, array, i) { + var len; - if ( args ) { - if ( isObj ) { - for ( name in object ) { - if ( callback.apply( object[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( object[ i++ ], args ) === false ) { - break; + // 检查传入的数组是否存在 + if (array) { + // 如果支持原生的 indexOf 方法 + if (indexOf) { + // 使用原生 indexOf 方法查找元素并返回索引 + return indexOf.call(array, elem, i); } - } - } - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in object ) { - if ( callback.call( object[ name ], name, object[ name ] ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { - break; + len = array.length; // 获取数组长度 + // 根据传入的索引 i 进行处理,支持负索引 + i = i ? i < 0 ? Math.max(0, len + i) : i : 0; + + // 遍历数组,查找与 elem 相等的元素 + for (; i < len; i++) { + // 跳过稀疏数组(即未定义的索引) + if (i in array && array[i] === elem) { + return i; // 找到元素,返回其索引 + } } } - } - } - - return object; - }, - // Use native String.trim function wherever possible - trim: trim ? - function( text ) { - return text == null ? - "" : - trim.call( text ); - } : - - // Otherwise use our own trimming functionality - function( text ) { - return text == null ? - "" : - text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); - }, + return -1; // 没有找到元素,返回 -1 + }, - // results is for internal usage only - makeArray: function( array, results ) { - var ret = results || []; +// 合并两个数组 + merge: function (first, second) { + var i = first.length, // 获取第一个数组的当前长度 + j = 0; // 初始化第二个数组的索引 - if ( array != null ) { - // The window, strings (and functions) also have 'length' - // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 - var type = jQuery.type( array ); + // 检查第二个参数是否为数组(即拥有 length 属性的对象) + if (typeof second.length === "number") { + // 遍历 second 数组,将其元素添加到 first 数组中 + for (var l = second.length; j < l; j++) { + first[i++] = second[j]; // 将 second 的元素推入 first + } + } else { + // 如果 second 不是一个有效的数组,使用 while 循环逐个添加元素 + while (second[j] !== undefined) { + first[i++] = second[j++]; // 添加元素直至遇到 undefined + } + } - if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { - push.call( ret, array ); - } else { - jQuery.merge( ret, array ); - } - } + first.length = i; // 更新 first 数组的长度 - return ret; - }, + return first; // 返回合并后的数组 + }, - inArray: function( elem, array, i ) { - var len; + // 定义一个 grep 函数,用于过滤数组中的元素 + grep: function (elems, callback, inv) { + var ret = [], // 初始化一个空数组,用于保存符合条件的元素 + retVal; // 用于存储回调函数的返回值 - if ( array ) { - if ( indexOf ) { - return indexOf.call( array, elem, i ); - } + inv = !!inv; // 将 inv 转换为布尔值,确保其为 true 或 false - len = array.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + // 遍历传入的数组 elems + for (var i = 0, length = elems.length; i < length; i++) { + // 调用回调函数,传入当前元素及其索引,并将返回值转换为布尔值 + retVal = !!callback(elems[i], i); - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in array && array[ i ] === elem ) { - return i; + // 根据 inv 的值决定是否加入 ret 数组 + if (inv !== retVal) { + ret.push(elems[i]); // 如果 inv 与 retVal 不同,则将当前元素添加到结果数组中 + } } - } - } - return -1; - }, - - merge: function( first, second ) { - var i = first.length, - j = 0; - - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; + return ret; // 返回符合条件的元素数组 + }, - return first; - }, + // arg 仅供内部使用 + map: function (elems, callback, arg) { + var value, // 用于存储回调函数返回的值 + key, // 用于遍历对象时的键 + ret = [], // 初始化一个空数组,用于保存结果 + i = 0, // 循环计数器 + length = elems.length, // 获取传入元素的长度 + // 检查 elems 是否为数组或 jQuery 对象 + isArray = elems instanceof jQuery || + (length !== undefined && typeof length === "number" && + ((length > 0 && elems[0] && elems[length - 1]) || + length === 0 || + jQuery.isArray(elems))); + + // 如果 elems 是数组或类数组对象 + if (isArray) { + // 遍历数组,调用回调函数,并处理每个元素 + for (; i < length; i++) { + value = callback(elems[i], i, arg); // 调用回调函数,传入当前元素、索引和 arg + + if (value != null) { // 检查返回值是否不为 null 或 undefined + ret[ret.length] = value; // 将有效的返回值添加到结果数组中 + } + } - grep: function( elems, callback, inv ) { - var ret = [], retVal; - inv = !!inv; + // 如果 elems 是对象 + } else { + // 遍历对象的每个键 + for (key in elems) { + value = callback(elems[key], key, arg); // 调用回调函数,传入当前属性值、键和 arg - // Go through the array, only saving the items - // that pass the validator function - for ( var i = 0, length = elems.length; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); - } - } + if (value != null) { // 检查返回值是否不为 null 或 undefined + ret[ret.length] = value; // 将有效的返回值添加到结果数组中 + } + } + } - return ret; - }, + // 扁平化任何嵌套数组,并返回结果数组 + return ret.concat.apply([], ret); + }, - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, key, ret = [], - i = 0, - length = elems.length, - // jquery objects are treated as arrays - isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + // 全局唯一标识符(GUID)计数器,用于对象 + guid: 1, - // Go through the array, translating each of the items to their - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); +// 将函数绑定到指定的上下文,且可选地部分应用参数 + proxy: function (fn, context) { + // 检查 context 是否为字符串,如果是,则将 fn 的相应方法作为目标函数 + if (typeof context === "string") { + var tmp = fn[context]; // 获取 fn 对象中 context 属性对应的方法 + context = fn; // 将上下文设置为 fn + fn = tmp; // 将 fn 更新为该属性方法 + } - if ( value != null ) { - ret[ ret.length ] = value; + // 快速检查目标函数是否可以调用,若不可调用则返回 undefined + if (!jQuery.isFunction(fn)) { + return undefined; // 如果 fn 不是函数,返回 undefined } - } - // Go through every key on the object, - } else { - for ( key in elems ) { - value = callback( elems[ key ], key, arg ); + // 模拟 bind 方法,创建一个代理函数 + var args = slice.call(arguments, 2), // 获取除了前两个参数外的其他所有参数 + proxy = function () { + // 在代理函数中调用原函数,并将上下文和参数传入 + return fn.apply(context, args.concat(slice.call(arguments))); + }; - if ( value != null ) { - ret[ ret.length ] = value; - } - } - } + // 将代理函数的 guid 设置为原始函数的 guid,这样可以通过该 guid 移除代理 + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; // 确保每个函数都有一个唯一的 guid - // Flatten any nested arrays - return ret.concat.apply( [], ret ); - }, + return proxy; // 返回代理函数 + }, - // A global GUID counter for objects - guid: 1, + // 多功能方法,用于获取和设置集合中的值 +// 值可以在函数的情况下选择性地被执行 + access: function (elems, fn, key, value, chainable, emptyGet, pass) { + var exec, // 用于判断是否执行函数值 + bulk = key == null, // 判断是否为批量操作,如果 key 为 null 则为批量操作 + i = 0, + length = elems.length; // 获取元素集合的长度 - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - if ( typeof context === "string" ) { - var tmp = fn[ context ]; - context = fn; - fn = tmp; - } + // 设置多个值 + if (key && typeof key === "object") { + for (i in key) { + // 对于对象中的每个键,递归调用 access 方法设置值 + jQuery.access(elems, fn, i, key[i], 1, emptyGet, value); + } + chainable = 1; // 设置链式调用标志 + + // 设置单个值 + } else if (value !== undefined) { + // 如果没有传递 pass 参数且 value 是一个函数,则执行该函数 + exec = pass === undefined && jQuery.isFunction(value); + + if (bulk) { + // 批量操作仅在执行函数值时进行迭代 + if (exec) { + exec = fn; // 保存原始的函数 + // 将 fn 替换为一个新的函数,该函数将在每个元素上调用 exec + fn = function (elem, key, value) { + return exec.call(jQuery(elem), value); + }; + + // 否则,对整个集合直接运行 + } else { + fn.call(elems, value); // 通过 fn 函数设置所有元素的值 + fn = null; // 将 fn 设为 null,表示不再需要它 + } + } - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } + if (fn) { + // 遍历 elements 数组,为每个元素调用 fn 函数,并传入相应的参数 + for (; i < length; i++) { + fn(elems[i], key, exec ? value.call(elems[i], i, fn(elems[i], key)) : value, pass); + } + } - // Simulated bind - var args = slice.call( arguments, 2 ), - proxy = function() { - return fn.apply( context, args.concat( slice.call( arguments ) ) ); - }; + chainable = 1; // 设置链式调用标志 + } - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + // 返回元素集合或获取值 + return chainable ? + elems : // 如果是链式调用,返回 elems + bulk ? + fn.call(elems) : // 如果是批量操作,则获取所有元素的值 + length ? fn(elems[0], key) : emptyGet; // 获取第一个元素的值,或者返回 emptyGet + }, - return proxy; - }, + now: function () { + // 返回当前时间的时间戳(自1970年1月1日以来的毫秒数) + return (new Date()).getTime(); + }, - // Mutifunctional method to get and set values to a collection - // The value/s can optionally be executed if it's a function - access: function( elems, fn, key, value, chainable, emptyGet, pass ) { - var exec, - bulk = key == null, - i = 0, - length = elems.length; - - // Sets many values - if ( key && typeof key === "object" ) { - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); - } - chainable = 1; - - // Sets one value - } else if ( value !== undefined ) { - // Optionally, function values get executed if exec is true - exec = pass === undefined && jQuery.isFunction( value ); - - if ( bulk ) { - // Bulk operations only iterate when executing function values - if ( exec ) { - exec = fn; - fn = function( elem, key, value ) { - return exec.call( jQuery( elem ), value ); - }; +// 使用 jQuery.browser 已被认为不推荐使用。 +// 更多详细信息请参见 http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function (ua) { + // 将用户代理字符串转为小写字母 + ua = ua.toLowerCase(); + + // 匹配用户代理中的浏览器信息 + var match = rwebkit.exec(ua) || // 检查是否为 WebKit 引擎 + ropera.exec(ua) || // 检查是否为 Opera 浏览器 + rmsie.exec(ua) || // 检查是否为 Internet Explorer + ua.indexOf("compatible") < 0 && rmozilla.exec(ua) || // 检查是否为 Mozilla 浏览器 + []; // 如果没有匹配项,则返回空数组 + + // 返回一个包含浏览器名称和版本号的对象 + return {browser: match[1] || "", version: match[2] || "0"}; + }, - // Otherwise they run against the entire set - } else { - fn.call( elems, value ); - fn = null; + sub: function () { + // 定义一个新的 jQuery 子类 + function jQuerySub(selector, context) { + // 返回一个新的 jQuerySub 实例 + return new jQuerySub.fn.init(selector, context); } - } - if ( fn ) { - for (; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); - } - } + // 扩展 jQuerySub,使其继承 jQuery 的属性和方法 + jQuery.extend(true, jQuerySub, this); + jQuerySub.superclass = this; // 保存父类引用 + jQuerySub.fn = jQuerySub.prototype = this(); // 设置原型链 - chainable = 1; - } + // 设置构造函数 + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; // 继承子类方法 - return chainable ? - elems : + // 初始化方法,处理上下文参数 + jQuerySub.fn.init = function init(selector, context) { + // 如果 context 是 jQuery 实例且不是 jQuerySub 实例,则转换为 jQuerySub 实例 + if (context && context instanceof jQuery && !(context instanceof jQuerySub)) { + context = jQuerySub(context); + } - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; - }, + // 调用父类的初始化方法 + return jQuery.fn.init.call(this, selector, context, rootjQuerySub); + }; - now: function() { - return ( new Date() ).getTime(); - }, + // 设置初始化原型 + jQuerySub.fn.init.prototype = jQuerySub.fn; - // Use of jQuery.browser is frowned upon. - // More details: http://docs.jquery.com/Utilities/jQuery.browser - uaMatch: function( ua ) { - ua = ua.toLowerCase(); + // 创建一个新的 jQuerySub 实例以作为根 + var rootjQuerySub = jQuerySub(document); + return jQuerySub; // 返回新的 jQuerySub 类 + }, - var match = rwebkit.exec( ua ) || - ropera.exec( ua ) || - rmsie.exec( ua ) || - ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || - []; + browser: {} // 定义一个空对象以存放浏览器相关的信息 + }); - return { browser: match[1] || "", version: match[2] || "0" }; - }, +// 填充 class2type 映射表 + jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + // 将 JavaScript 数据类型的字符串映射到对应的小写名称 + class2type[ "[object " + name + "]" ] = name.toLowerCase(); + }); - sub: function() { - function jQuerySub( selector, context ) { - return new jQuerySub.fn.init( selector, context ); +// 匹配用户代理信息 + browserMatch = jQuery.uaMatch( userAgent ); + if ( browserMatch.browser ) { + // 如果找到浏览器类型,则将其标记为 true,并设置版本号 + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; } - jQuery.extend( true, jQuerySub, this ); - jQuerySub.superclass = this; - jQuerySub.fn = jQuerySub.prototype = this(); - jQuerySub.fn.constructor = jQuerySub; - jQuerySub.sub = this.sub; - jQuerySub.fn.init = function init( selector, context ) { - if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { - context = jQuerySub( context ); - } - - return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); - }; - jQuerySub.fn.init.prototype = jQuerySub.fn; - var rootjQuerySub = jQuerySub(document); - return jQuerySub; - }, - browser: {} -}); +// 已被弃用,使用 jQuery.browser.webkit 替代 + if ( jQuery.browser.webkit ) { + // 如果是 WebKit 浏览器,则标记为 Safari + jQuery.browser.safari = true; + } -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); +// 对于 IE,不会将非断行空格与 \s 匹配 + if ( rnotwhite.test( "\xA0" ) ) { + // 设置左侧和右侧的 trim 正则表达式,以去除空格和非断行空格 + trimLeft = /^[\s\xA0]+/; // 匹配开头的空格和非断行空格 + trimRight = /[\s\xA0]+$/; // 匹配结尾的空格和非断行空格 + } -browserMatch = jQuery.uaMatch( userAgent ); -if ( browserMatch.browser ) { - jQuery.browser[ browserMatch.browser ] = true; - jQuery.browser.version = browserMatch.version; -} +// 所有 jQuery 对象都应指向这个根 jQuery 实例 + rootjQuery = jQuery(document); -// Deprecated, use jQuery.browser.webkit instead -if ( jQuery.browser.webkit ) { - jQuery.browser.safari = true; -} +// 文档就绪方法的清理函数 + if ( document.addEventListener ) { + // 如果支持 addEventListener 方法 + DOMContentLoaded = function() { + // 移除 DOMContentLoaded 事件监听 + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + // 调用 jQuery 的就绪方法 + jQuery.ready(); + }; -// IE doesn't match non-breaking spaces with \s -if ( rnotwhite.test( "\xA0" ) ) { - trimLeft = /^[\s\xA0]+/; - trimRight = /[\s\xA0]+$/; -} + } else if ( document.attachEvent ) { + // 如果支持 attachEvent 方法(主要针对 IE) + DOMContentLoaded = function() { + // 确保文档状态为完整(complete),以避免 IE 过于积极的操作 + if ( document.readyState === "complete" ) { + // 移除 onreadystatechange 事件监听 + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + // 调用 jQuery 的就绪方法 + jQuery.ready(); + } + }; + } -// All jQuery objects should point back to these -rootjQuery = jQuery(document); +// 检查 DOM 是否准备就绪,专门针对 Internet Explorer + function doScrollCheck() { + if ( jQuery.isReady ) { + // 如果 jQuery 已经准备好,则直接返回 + return; + } -// Cleanup functions for the document ready method -if ( document.addEventListener ) { - DOMContentLoaded = function() { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - }; + try { + // 如果使用的是 IE,采用 Diego Perini 的技巧来检查内容加载 + // 参考链接: http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + // 如果出现异常,设置延时调用 doScrollCheck 函数,以继续检查 + setTimeout( doScrollCheck, 1 ); + return; + } -} else if ( document.attachEvent ) { - DOMContentLoaded = function() { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( document.readyState === "complete" ) { - document.detachEvent( "onreadystatechange", DOMContentLoaded ); + // 一旦 DOM 加载完毕,执行所有等待中的函数 jQuery.ready(); } - }; -} - -// The DOM ready check for Internet Explorer -function doScrollCheck() { - if ( jQuery.isReady ) { - return; - } - - try { - // If IE is used, use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - document.documentElement.doScroll("left"); - } catch(e) { - setTimeout( doScrollCheck, 1 ); - return; - } - - // and execute any waiting functions - jQuery.ready(); -} - -return jQuery; -})(); +// 返回 jQuery 对象(这部分通常是立即调用的函数表达式的一部分) + return jQuery; + })(); -// String to Object flags format cache -var flagsCache = {}; +// 字符串到对象的标志格式缓存 + var flagsCache = {}; -// Convert String-formatted flags into Object-formatted ones and store in cache -function createFlags( flags ) { - var object = flagsCache[ flags ] = {}, - i, length; - flags = flags.split( /\s+/ ); - for ( i = 0, length = flags.length; i < length; i++ ) { - object[ flags[i] ] = true; +// 将字符串格式的标志转换为对象格式,并存储在缓存中 + function createFlags( flags ) { + var object = flagsCache[ flags ] = {}, // 创建一个新的对象并存入缓存 + i, length; + // 将传入的字符串按空格分割成数组 + flags = flags.split( /\s+/ ); + for ( i = 0, length = flags.length; i < length; i++ ) { + // 遍历每个标志,将其作为对象的属性,值为 true + object[ flags[i] ] = true; + } + return object; // 返回转换后的对象 } - return object; -} + /* * Create a callback list using the following parameters: @@ -1019,203 +1257,228 @@ function createFlags( flags ) { * stopOnFalse: interrupt callings when a callback returns false * */ -jQuery.Callbacks = function( flags ) { - - // Convert flags from String-formatted to Object-formatted - // (we check in cache first) - flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; - - var // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = [], - // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Add one or several callbacks to the list - add = function( args ) { - var i, - length, - elem, - type, - actual; - for ( i = 0, length = args.length; i < length; i++ ) { - elem = args[ i ]; - type = jQuery.type( elem ); - if ( type === "array" ) { - // Inspect recursively - add( elem ); - } else if ( type === "function" ) { - // Add if not in unique mode and callback is not in - if ( !flags.unique || !self.has( elem ) ) { - list.push( elem ); + jQuery.Callbacks = function( flags ) { + + // 将字符串格式的标志转换为对象格式 + // 首先在缓存中检查 + flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; + + var // 实际的回调列表 + list = [], + // 用于重复调用的堆栈 + stack = [], + // 最后一次触发的值(用于不可遗忘列表) + memory, + // 标记以知晓列表是否已经触发 + fired, + // 标记以知晓列表是否正在触发中 + firing, + // 第一个要触发的回调(由 add 和 fireWith 内部使用) + firingStart, + // 触发时循环的结束 + firingLength, + // 当前正在执行的回调索引(可被 remove 修改) + firingIndex, + // 向列表添加一个或多个回调的函数 + add = function( args ) { + var i, + length, + elem, + type, + actual; + // 遍历传入的参数数组 + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; // 获取当前元素 + type = jQuery.type( elem ); // 检查元素类型 + if ( type === "array" ) { + // 如果元素是数组,递归调用 add 函数 + add( elem ); + } else if ( type === "function" ) { + // 如果元素是函数且不是唯一模式下,且回调不在列表中,则添加到列表 + if ( !flags.unique || !self.has( elem ) ) { + list.push( elem ); // 将函数添加到回调列表中 + } } } - } - }, - // Fire callbacks + }; + + // 执行回调函数 fire = function( context, args ) { - args = args || []; - memory = !flags.memory || [ context, args ]; - fired = true; - firing = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; + args = args || []; // 确保 args 是一个数组,如果没有传入则初始化为空数组 + memory = !flags.memory || [ context, args ]; // 根据 flags.memory 标志决定是否记录触发的上下文和参数 + fired = true; // 标记为已触发 + firing = true; // 标记为正在触发 + firingIndex = firingStart || 0; // 初始化当前索引,firingStart 为 0 或之前保存的值 + firingStart = 0; // 重置触发开始的位置 + firingLength = list.length; // 获取回调列表的长度 + + // 遍历回调列表并执行每个函数 for ( ; list && firingIndex < firingLength; firingIndex++ ) { + // 执行当前索引的回调函数,并传递上下文和参数 if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { - memory = true; // Mark as halted - break; + memory = true; // 如果返回 false 且设置了 stopOnFalse,标记为中止 + break; // 中止循环 } } - firing = false; - if ( list ) { - if ( !flags.once ) { - if ( stack && stack.length ) { - memory = stack.shift(); - self.fireWith( memory[ 0 ], memory[ 1 ] ); + + firing = false; // 结束触发状态 + + if ( list ) { // 如果回调列表存在 + if ( !flags.once ) { // 如果未设置一次性标志 + if ( stack && stack.length ) { // 检查堆栈是否存在且有元素 + memory = stack.shift(); // 从堆栈中取出下一个要触发的记忆(上下文和参数) + self.fireWith( memory[ 0 ], memory[ 1 ] ); // 以新的上下文和参数再次触发回调 } - } else if ( memory === true ) { - self.disable(); + } else if ( memory === true ) { // 如果 memory 为 true,表示触发完成 + self.disable(); // 禁用回调列表 } else { - list = []; + list = []; // 在一次性情况下清空回调列表 } } }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - var length = list.length; - add( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away, unless previous - // firing was halted (stopOnFalse) - } else if ( memory && memory !== true ) { - firingStart = length; - fire( memory[ 0 ], memory[ 1 ] ); + + // 实际的回调对象 + self = { + // 添加一个回调或一组回调到列表中 + add: function() { + if ( list ) { // 检查回调列表是否存在 + var length = list.length; // 获取当前回调列表的长度 + add( arguments ); // 调用 add 函数,将传入的参数添加到回调列表 + + // 我们需要将回调添加到当前正在触发的批次吗? + if ( firing ) { + firingLength = list.length; // 如果正在触发,更新当前回调列表的长度 + // 如果有记忆值,且当前没有触发, + // 则应该立即调用回调,除非之前的触发被中止(stopOnFalse) + } else if ( memory && memory !== true ) { + firingStart = length; // 设置触发开始的位置为当前列表的长度 + fire( memory[ 0 ], memory[ 1 ] ); // 立即触发回调,使用记忆中的上下文和参数 + } } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - var args = arguments, - argIndex = 0, - argLength = args.length; - for ( ; argIndex < argLength ; argIndex++ ) { - for ( var i = 0; i < list.length; i++ ) { - if ( args[ argIndex ] === list[ i ] ) { - // Handle firingIndex and firingLength - if ( firing ) { - if ( i <= firingLength ) { - firingLength--; - if ( i <= firingIndex ) { - firingIndex--; + return this; // 返回当前对象,以支持链式调用 + }, + + // 从列表中移除一个回调 + remove: function() { + if ( list ) { // 检查回调列表是否存在 + var args = arguments, // 获取传入的参数 + argIndex = 0, // 当前参数索引 + argLength = args.length; // 参数的总长度 + + // 遍历所有传入的参数 + for ( ; argIndex < argLength ; argIndex++ ) { + // 对于每个参数,遍历回调列表 + for ( var i = 0; i < list.length; i++ ) { + // 如果当前参数与回调列表中的某个回调相等 + if ( args[ argIndex ] === list[ i ] ) { + // 处理 firingIndex 和 firingLength 的更新 + if ( firing ) { + // 当前正在触发,并且要移除的回调在触发列表之前 + if ( i <= firingLength ) { + firingLength--; // 更新触发长度 + if ( i <= firingIndex ) { + firingIndex--; // 更新触发索引 + } } } - } - // Remove the element - list.splice( i--, 1 ); - // If we have some unicity property then - // we only need to do this once - if ( flags.unique ) { - break; + // 移除该元素 + list.splice( i--, 1 ); // 从列表中移除元素,并将索引减一以调整循环 + + // 如果设置了唯一性标志,那么只需移除一次 + if ( flags.unique ) { + break; // 跳出当前循环,不再检查其他相同的回调 + } } } } } - } - return this; - }, - // Control if a given callback is in the list - has: function( fn ) { - if ( list ) { - var i = 0, - length = list.length; - for ( ; i < length; i++ ) { - if ( fn === list[ i ] ) { - return true; + return this; // 返回当前对象,以支持链式调用 + }, + + // 控制特定回调是否在列表中 + has: function( fn ) { + if ( list ) { // 检查回调列表是否存在 + var i = 0, + length = list.length; // 获取列表的长度 + // 遍历回调列表 + for ( ; i < length; i++ ) { + // 如果找到与给定回调相同的项,则返回 true + if ( fn === list[ i ] ) { + return true; + } } } - } - return false; - }, - // Remove all callbacks from the list - empty: function() { - list = []; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory || memory === true ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( stack ) { - if ( firing ) { - if ( !flags.once ) { - stack.push( [ context, args ] ); + return false; // 如果没有找到,返回 false + }, + +// 从列表中移除所有回调 + empty: function() { + list = []; // 将列表清空 + return this; // 返回当前对象,以支持链式调用 + }, + +// 使列表不再执行任何操作 + disable: function() { + list = stack = memory = undefined; // 将列表、栈和内存设为 undefined + return this; // 返回当前对象,以支持链式调用 + }, + +// 检查是否已禁用 + disabled: function() { + return !list; // 如果列表不存在,则返回 true + }, + +// 锁定列表,使其处于当前状态 + lock: function() { + stack = undefined; // 清空栈 + // 如果没有内存或内存为 true,则禁用列表 + if ( !memory || memory === true ) { + self.disable(); // 调用 disable 方法 + } + return this; // 返回当前对象,以支持链式调用 + }, + +// 检查列表是否被锁定 + locked: function() { + return !stack; // 如果栈不存在,则返回 true + }, + + // 使用给定的上下文和参数调用所有回调 + fireWith: function( context, args ) { + if ( stack ) { // 检查栈是否存在 + if ( firing ) { // 检查是否正在执行回调 + // 如果当前正在执行且未设置一次性标志,推入新的上下文和参数到栈中 + if ( !flags.once ) { + stack.push( [ context, args ] ); + } + } else if ( !( flags.once && memory ) ) { // 如果未设置一次性标志且没有内存记录 + fire( context, args ); // 立即调用回调函数 } - } else if ( !( flags.once && memory ) ) { - fire( context, args ); } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; + return this; // 返回当前对象,以支持链式调用 + }, - return self; -}; +// 使用给定的参数调用所有回调 + fire: function() { + self.fireWith( this, arguments ); // 调用 fireWith 方法,传入当前上下文和参数 + return this; // 返回当前对象,以支持链式调用 + }, +// 检查回调是否至少已被调用过一次 + fired: function() { + return !!fired; // 将 fired 转换为布尔值返回,表明回调是否已被调用 + } + }; + return self; + }; -var // Static reference to slice + + var // Static reference to slice sliceDeferred = [].slice; -jQuery.extend({ + jQuery.extend({ Deferred: function( func ) { var doneList = jQuery.Callbacks( "once memory" ), @@ -2128,7 +2391,7 @@ jQuery.extend({ } } }); - +//a jQuery.fn.extend({ queue: function( type, data ) { var setter = 2;