wjy_branch
wangjiaoyan 3 weeks ago
parent 0b999cda56
commit e902a1e06b

@ -2000,249 +2000,297 @@
// 正则表达式,用于检测字符串是否为有效的 JSON 对象或数组
var rbrace = /^(?:\{.*\}|\[.*\])$/,
rmultiDash = /([A-Z])/g;
// 正则表达式,用于匹配大写字母
rmultiDash = /([A-Z])/g;
jQuery.extend({
cache: {},
// Please use with caution
uuid: 0,
// Unique for each copy of jQuery on the page
// Non-digits removed to match rinlinejQuery
expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
// 扩展 jQuery 对象,添加新属性和方法
jQuery.extend({
// 用于缓存元素及其相关数据的对象
cache: {},
// 唯一标识符,每次调用会递增
// 请谨慎使用
uuid: 0,
// 页面上每个 jQuery 实例的唯一标识符
// 使用了随机数去除非数字字符,以匹配 rinlinejQuery
expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
// 以下元素如果尝试添加 expando 属性,会引发无法捕获的异常
noData: {
// 禁止在 embed 元素上设置 expando 属性
"embed": true,
// 除 Flash 外,禁止在所有对象上设置 expando 属性
"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
// 禁止在 applet 元素上设置 expando 属性
"applet": true
},
// The following elements throw uncatchable exceptions if you
// attempt to add expando properties to them.
noData: {
"embed": true,
// Ban all objects except for Flash (which handle expandos)
"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
"applet": true
},
// 检查指定元素是否包含数据
hasData: function( elem ) {
// 如果 elem 是一个节点,则从缓存中获取相应的数据
// 否则直接从 elem 的 expando 属性中获取数据
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
// 如果 elem 存在且不是空数据对象,则返回 true
return !!elem && !isEmptyDataObject( elem );
},
hasData: function( elem ) {
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
return !!elem && !isEmptyDataObject( elem );
},
// 定义 data 函数,用于在元素或对象上存储或获取数据
data: function( elem, name, data, pvt /* 内部使用 */ ) {
// 检查元素是否可以接受数据,如果不可以则返回
if ( !jQuery.acceptData( elem ) ) {
return;
}
data: function( elem, name, data, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
var privateCache, thisCache, ret,
internalKey = jQuery.expando, // 获取唯一标识符
getByName = typeof name === "string", // 检查 name 是否为字符串
var privateCache, thisCache, ret,
internalKey = jQuery.expando,
getByName = typeof name === "string",
// 处理 DOM 节点和 JS 对象的不同情况,因为 IE6-7
// 无法在 DOM 和 JS 边界之间正确进行垃圾回收
isNode = elem.nodeType, // 判断 elem 是否为 DOM 节点
// We have to handle DOM nodes and JS objects differently because IE6-7
// can't GC object references properly across the DOM-JS boundary
isNode = elem.nodeType,
// 只有 DOM 节点需要全局 jQuery 缓存JS 对象的数据
// 直接附加到对象上,因此可以自动进行垃圾回收
cache = isNode ? jQuery.cache : elem, // 根据 elem 类型选择缓存
// Only DOM nodes need the global jQuery cache; JS object data is
// attached directly to the object so GC can occur automatically
cache = isNode ? jQuery.cache : elem,
// 只有当 JS 对象的缓存已经存在时,才为其定义 ID
// 这样可以让代码在没有缓存的 DOM 节点路径上快速处理
id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
isEvents = name === "events"; // 检查 name 是否等于 "events"
// Only defining an ID for JS objects if its cache already exists allows
// the code to shortcut on the same path as a DOM node with no cache
id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
isEvents = name === "events";
// 在尝试获取没有数据的对象时,避免做不必要的工作
if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
return; // 如果没有找到数据或条件不满足,返回
}
// Avoid doing any more work than we need to when trying to get data on an
// object that has no data at all
if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
return;
}
if ( !id ) {
// Only DOM nodes need a new unique ID for each element since their data
// ends up in the global cache
if ( isNode ) {
elem[ internalKey ] = id = ++jQuery.uuid;
} else {
id = internalKey;
if ( !id ) {
// 只有 DOM 节点需要为每个元素生成一个新的唯一 ID
// 因为它们的数据最终会存储在全局缓存中
if ( isNode ) {
elem[ internalKey ] = id = ++jQuery.uuid; // 为 DOM 节点分配唯一的 ID
} else {
id = internalKey; // 对于非 DOM 对象,使用内部键作为 ID
}
}
}
if ( !cache[ id ] ) {
cache[ id ] = {};
if ( !cache[ id ] ) {
cache[ id ] = {}; // 初始化缓存对象,如果该 ID 的缓存不存在
// Avoids exposing jQuery metadata on plain JS objects when the object
// is serialized using JSON.stringify
if ( !isNode ) {
cache[ id ].toJSON = jQuery.noop;
// 避免在使用 JSON.stringify 序列化普通 JS 对象时暴露 jQuery 元数据
if ( !isNode ) {
cache[ id ].toJSON = jQuery.noop; // 如果不是 DOM 节点,则定义 toJSON 方法为无操作
}
}
}
// An object can be passed to jQuery.data instead of a key/value pair; this gets
// shallow copied over onto the existing cache
if ( typeof name === "object" || typeof name === "function" ) {
if ( pvt ) {
cache[ id ] = jQuery.extend( cache[ id ], name );
} else {
cache[ id ].data = jQuery.extend( cache[ id ].data, name );
// 可以将一个对象传递给 jQuery.data而不是键/值对;
// 这个对象会被浅拷贝到现有的缓存中
if ( typeof name === "object" || typeof name === "function" ) {
if ( pvt ) {
// 如果是私有数据,则直接扩展当前 ID 的缓存
cache[ id ] = jQuery.extend( cache[ id ], name );
} else {
// 否则,将数据扩展到现有的 data 属性上
cache[ id ].data = jQuery.extend( cache[ id ].data, name );
}
}
}
privateCache = thisCache = cache[ id ];
// jQuery data() is stored in a separate object inside the object's internal data
// cache in order to avoid key collisions between internal data and user-defined
// data.
if ( !pvt ) {
if ( !thisCache.data ) {
thisCache.data = {};
}
privateCache = thisCache = cache[ id ]; // 初始化 privateCache 和 thisCache指向指定 ID 的缓存对象
thisCache = thisCache.data;
}
// jQuery 的 data() 方法存储在对象内部数据缓存中的一个单独对象中
// 以避免内部数据和用户定义数据之间的键冲突。
if ( !pvt ) {
// 如果不是私有数据,则为 thisCache 初始化一个 data 对象
if ( !thisCache.data ) {
thisCache.data = {}; // 创建一个空的数据对象
}
if ( data !== undefined ) {
thisCache[ jQuery.camelCase( name ) ] = data;
}
thisCache = thisCache.data; // 将 thisCache 指向 data 对象
}
// Users should not attempt to inspect the internal events object using jQuery.data,
// it is undocumented and subject to change. But does anyone listen? No.
if ( isEvents && !thisCache[ name ] ) {
return privateCache.events;
}
// 如果 data 参数不为 undefined则将其存储在 thisCache 中
if ( data !== undefined ) {
thisCache[ jQuery.camelCase( name ) ] = data; // 使用驼峰命名法格式化名称,并赋值
}
// Check for both converted-to-camel and non-converted data property names
// If a data property was specified
if ( getByName ) {
// 用户不应尝试通过 jQuery.data 检查内部事件对象,
// 该对象未记录并可能会更改。但真的有人听从吗?没有。
if ( isEvents && !thisCache[ name ] ) {
return privateCache.events; // 如果是事件并且 thisCache 中不存在该名称,则返回 privateCache 的 events
}
// First Try to find as-is property data
ret = thisCache[ name ];
// 检查数据属性名称,既包括未转换的原始名称也包括转换为驼峰命名法的名称
// 如果指定了数据属性
if ( getByName ) {
// Test for null|undefined property data
if ( ret == null ) {
// 首先尝试查找原始名称对应的数据属性
ret = thisCache[ name ];
// Try to find the camelCased property
ret = thisCache[ jQuery.camelCase( name ) ];
// 测试获取到的属性数据是否为 null 或 undefined
if ( ret == null ) {
// 尝试查找驼峰命名法格式化后的属性
ret = thisCache[ jQuery.camelCase( name ) ];
}
} else {
// 如果没有指定获取名称,直接返回 thisCache
ret = thisCache;
}
} else {
ret = thisCache;
}
return ret;
},
// 返回结果
return ret;
},
removeData: function( elem, name, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
removeData: function( elem, name, pvt /* 仅供内部使用 */ ) {
// 检查元素是否可以接受数据
if ( !jQuery.acceptData( elem ) ) {
return; // 如果不能接受数据,则退出
}
var thisCache, i, l,
var thisCache, i, l, // 声明变量以用于后续操作
// Reference to internal data cache key
internalKey = jQuery.expando,
isNode = elem.nodeType,
// 内部数据缓存的键引用
internalKey = jQuery.expando,
// See jQuery.data for more information
cache = isNode ? jQuery.cache : elem,
// 检查元素类型,确定是否为节点
isNode = elem.nodeType,
// See jQuery.data for more information
id = isNode ? elem[ internalKey ] : internalKey;
// 根据元素类型选择缓存对象,节点使用 jQuery.cache其他元素直接使用 elem
cache = isNode ? jQuery.cache : elem,
// If there is already no cache entry for this object, there is no
// purpose in continuing
if ( !cache[ id ] ) {
return;
}
// 获取元素的唯一标识符,如果是节点,则从元素中获取 internalKey否则使用 internalKey 本身
id = isNode ? elem[ internalKey ] : internalKey;
if ( name ) {
// 如果该对象已经没有缓存条目,继续执行没有意义
if ( !cache[ id ] ) {
return; // 退出函数
}
thisCache = pvt ? cache[ id ] : cache[ id ].data;
// 如果指定了名称name则根据 pvt 选择不同的数据缓存
if ( name ) {
// 根据是否是私有数据选择对应的缓存
thisCache = pvt ? cache[ id ] : cache[ id ].data;
if ( thisCache ) {
// Support array or space separated string names for data keys
if ( !jQuery.isArray( name ) ) {
// 检查 thisCache 是否存在
if ( thisCache ) {
// try the string as a key before any manipulation
if ( name in thisCache ) {
name = [ name ];
} else {
// 支持数组或以空格分隔的字符串作为数据键名
if ( !jQuery.isArray( name ) ) {
// split the camel cased version by spaces unless a key with the spaces exists
name = jQuery.camelCase( name );
// 尝试将字符串作为键值直接访问,如果存在,则将其转换为数组
if ( name in thisCache ) {
name = [ name ];
name = [ name ]; // 将单个键封装成数组
} else {
name = name.split( " " );
// 对名称进行驼峰命名法处理,并再次检查该键是否存在于 thisCache 中
name = jQuery.camelCase( name );
if ( name in thisCache ) {
name = [ name ]; // 如果存在,封装成数组
} else {
// 如果仍然不存在,按空格分割字符串为多个键名
name = name.split( " " );
}
}
}
}
for ( i = 0, l = name.length; i < l; i++ ) {
delete thisCache[ name[i] ];
}
// 遍历所有要删除的键名,并从 thisCache 中删除对应的条目
for ( i = 0, l = name.length; i < l; i++ ) {
delete thisCache[ name[i] ];
}
// If there is no data left in the cache, we want to continue
// and let the cache object itself get destroyed
if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
return;
// 如果缓存中没有剩余的数据,我们希望继续执行并让缓存对象本身被销毁
if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
return; // 如果缓存不为空,则退出函数
}
}
}
}
// See jQuery.data for more information
if ( !pvt ) {
delete cache[ id ].data;
// Don't destroy the parent cache unless the internal data object
// had been the only thing left in it
if ( !isEmptyDataObject(cache[ id ]) ) {
return;
// See jQuery.data for more information
// 首先判断pvt是否为假即不存在或值为false等情况
if (!pvt) {
// 如果pvt不存在从名为cache的对象中根据id删除其data属性
delete cache[id].data;
// Don't destroy the parent cache unless the internal data object
// had been the only thing left in it
// 以下判断表示只有当cache[id]这个对象内部的数据对象也就是刚删除data属性后的cache[id]已经为空即除了刚删除的data属性外没别的内容了才继续往下执行删除整个cache[id]的操作;
// 如果cache[id]还有其他属性等内容存在就直接返回不进行后续删除整个cache[id]的操作)
if (!isEmptyDataObject(cache[id])) {
return;
}
}
}
// Browsers that fail expando deletion also refuse to delete expandos on
// the window, but it will allow it on all other JS objects; other browsers
// don't care
// Ensure that `cache` is not a window object #10080
if ( jQuery.support.deleteExpando || !cache.setInterval ) {
delete cache[ id ];
} else {
cache[ id ] = null;
}
// We destroyed the cache and need to eliminate the expando on the node to avoid
// false lookups in the cache for entries that no longer exist
if ( isNode ) {
// IE does not allow us to delete expando properties from nodes,
// nor does it have a removeAttribute function on Document nodes;
// we must handle all of these cases
if ( jQuery.support.deleteExpando ) {
delete elem[ internalKey ];
} else if ( elem.removeAttribute ) {
elem.removeAttribute( internalKey );
// Browsers that fail expando deletion also refuse to delete expandos on
// the window, but it will allow it on all other JS objects; other browsers
// don't care
// Ensure that `cache` is not a window object #10080
// 这里进行条件判断目的是决定如何处理从cache对象中删除以id为键的元素
// 如果浏览器支持删除扩展属性jQuery.support.deleteExpando为真或者cache对象不存在setInterval属性意味着它不是window对象因为window对象有setInterval方法
if (jQuery.support.deleteExpando ||!cache.setInterval) {
// 那么直接从cache对象中删除以id为键的这个元素
delete cache[id];
} else {
elem[ internalKey ] = null;
// 否则也就是不满足上述条件可能是浏览器不支持删除扩展属性且cache对象是window对象这种情况将cache[id]赋值为null相当于进行一种间接的“清除”处理虽然不能直接删除window对象上的属性但可以通过赋值为null来达到类似效果
cache[id] = null;
}
// We destroyed the cache and need to eliminate the expando on the node to avoid
// false lookups in the cache for entries that no longer exist
// 这段注释说明当缓存已经被销毁时需要移除节点上的扩展属性expando以避免在缓存中对已不存在的条目进行错误查找。
if (isNode) {
// 如果当前元素是节点isNode为真以下代码处理不同浏览器环境下移除节点上特定扩展属性的情况。
// IE does not allow us to delete expando properties from nodes,
// nor does it have a removeAttribute function on Document nodes;
// we must handle all of these cases
// 如果浏览器支持删除扩展属性jQuery.support.deleteExpando为真
if (jQuery.support.deleteExpando) {
// 直接从elem元素对象上删除名为internalKey的属性这个internalKey应该是用于存储某种扩展数据相关的键
delete elem[internalKey];
} else if (elem.removeAttribute) {
// 如果不支持直接删除扩展属性但elem元素有removeAttribute方法
// 则使用removeAttribute方法来移除名为internalKey的属性达到类似效果。
elem.removeAttribute(internalKey);
} else {
// 如果前面两种情况都不满足既不支持直接删除扩展属性也没有removeAttribute方法可用
// 则将elem[internalKey]赋值为null进行一种间接的“清除”处理尽量避免后续可能出现的问题。
elem[internalKey] = null;
}
}
}
},
},
// For internal use only.
_data: function( elem, name, data ) {
return jQuery.data( elem, name, data, true );
},
// For internal use only.
// 以下方法被标记为仅供内部使用从函数的实现来看它实际上是调用了jQuery.data方法并传入了额外的参数true
// 具体功能应该和jQuery.data相关联可能在内部处理数据时有一些特殊逻辑。
_data: function(elem, name, data) {
return jQuery.data(elem, name, data, true);
},
// A method for determining if a DOM node can handle the data expando
acceptData: function( elem ) {
if ( elem.nodeName ) {
var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
// A method for determining if a DOM node can handle the data expando
// 这个方法用于判断一个DOM节点是否能够处理数据扩展属性data expando
acceptData: function(elem) {
if (elem.nodeName) {
// 获取elem节点名称的小写形式对应的在jQuery.noData对象中的匹配项
// jQuery.noData应该是一个存储了哪些节点名称不适合处理数据扩展属性相关信息的对象。
var match = jQuery.noData[elem.nodeName.toLowerCase()];
if ( match ) {
return !(match === true || elem.getAttribute("classid") !== match);
if (match) {
// 如果存在匹配项,根据匹配项的值来返回判断结果。
// 如果match为true或者elem节点的classid属性值不等于match时返回false表示该节点不能处理数据扩展属性
// 否则返回true。
return!(match === true || elem.getAttribute("classid")!== match);
}
}
// 如果前面没有匹配到不适合处理的情况或者节点本身没有节点名称elem.nodeName不存在默认返回true表示可以处理数据扩展属性。
return true;
}
});
return true;
}
});
jQuery.fn.extend({
data: function( key, value ) {

Loading…
Cancel
Save