|
|
|
@ -877,93 +877,99 @@
|
|
|
|
|
* @returns {function(string, object)} 返回一个在存储后将数据对象存储在自身上的函数,属性名为(空格后缀)字符串,
|
|
|
|
|
* 如果缓存大于Expr.cacheLength,则删除最旧的条目
|
|
|
|
|
*/
|
|
|
|
|
// `createCache`函数用于创建一个缓存机制相关的函数,通过返回的函数可以实现对数据的缓存,并根据一定规则管理缓存的大小,避免缓存无限增长。
|
|
|
|
|
function createCache() {
|
|
|
|
|
// 创建一个空数组`keys`,用于存储缓存的键(后续会在缓存操作中记录每个缓存项对应的键,方便进行缓存的管理,比如删除过期的缓存项等操作)。
|
|
|
|
|
var keys = [];
|
|
|
|
|
|
|
|
|
|
// 定义内部函数`cache`,它是实际用于操作缓存的函数,接收两个参数,`key`表示缓存项的键,`value`表示要缓存的值。
|
|
|
|
|
function cache( key, value ) {
|
|
|
|
|
// 使用(key + " ")以避免与原生原型属性冲突(见Issue #157)
|
|
|
|
|
// 使用`(key + " ")`的方式来作为实际存储在缓存对象中的键,这样做的目的是避免与原生原型属性冲突(见Issue #157,这里应该是在项目的相关问题记录中提到了这种处理方式是为了解决和原生属性冲突的问题)。
|
|
|
|
|
// 判断存储键的`keys`数组长度,当通过`keys.push(key + " ")`将新的键添加到`keys`数组后,如果数组长度大于`Expr.cacheLength`(这里`Expr`应该是外部定义的一个对象,`cacheLength`是它上面定义的用于控制缓存大小的属性,表示缓存最多能存储的项数,具体数值由外部设定),说明缓存项过多,需要进行清理操作。
|
|
|
|
|
if ( keys.push( key + " " ) > Expr.cacheLength ) {
|
|
|
|
|
// 仅保留最近的条目
|
|
|
|
|
// 仅保留最近的条目,通过`delete cache[keys.shift()]`操作,从`cache`对象(也就是这个函数自身,在JavaScript中函数也是对象,可以像对象一样存储属性,这里把`cache`当作缓存对象来存储键值对)中删除最早添加的缓存项(通过`keys.shift()`获取并移除`keys`数组中的第一个元素,也就是最早添加的键,然后用这个键从`cache`对象中删除对应的缓存项)。
|
|
|
|
|
delete cache[ keys.shift() ];
|
|
|
|
|
}
|
|
|
|
|
// 将传入的`value`值存储到`cache`对象中,以`(key + " ")`为键,并返回这个存储的值(方便在调用`cache`函数时能直接获取到存储后的结果,例如可以链式操作等),这样就完成了一次缓存的添加或者更新操作。
|
|
|
|
|
return (cache[ key + " " ] = value);
|
|
|
|
|
}
|
|
|
|
|
// 返回内部定义的`cache`函数,外部代码可以通过调用返回的这个函数来进行缓存相关的操作,比如添加缓存、获取缓存等,并且缓存会按照上述规则自动管理大小。
|
|
|
|
|
return cache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 为Sizzle特殊使用标记一个函数
|
|
|
|
|
* @param {Function} fn 要标记的函数
|
|
|
|
|
*/
|
|
|
|
|
function markFunction( fn ) {
|
|
|
|
|
fn[ expando ] = true;
|
|
|
|
|
return fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 使用一个元素支持测试
|
|
|
|
|
* @param {Function} fn 传入创建的div,并期望一个布尔结果
|
|
|
|
|
*/
|
|
|
|
|
function assert( fn ) {
|
|
|
|
|
var div = document.createElement("div");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return !!fn( div );
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
// 默认情况下从其父节点中移除
|
|
|
|
|
if ( div.parentNode ) {
|
|
|
|
|
div.parentNode.removeChild( div );
|
|
|
|
|
// `markFunction`函数用于为Sizzle特殊使用标记一个函数,通过给函数添加一个特定的属性(使用`expando`属性,前面代码中应该有其定义,通常是一个独特的、用于标识等用途的自定义属性)来标记该函数,方便在后续Sizzle相关的逻辑中识别这个函数有特殊用途。
|
|
|
|
|
// 参数`fn`是要标记的函数,也就是要添加特殊标识的函数对象。
|
|
|
|
|
function markFunction( fn ) {
|
|
|
|
|
// 给传入的函数`fn`添加一个名为`expando`的属性,并将其值设置为`true`,以此来标记该函数在Sizzle中有特殊用途。
|
|
|
|
|
fn[ expando ] = true;
|
|
|
|
|
// 返回标记后的函数,这样外部代码在调用这个函数时可以知道它已经被标记过了,并且可以继续按照原函数的逻辑进行使用(例如传递给其他Sizzle相关的函数等操作)。
|
|
|
|
|
return fn;
|
|
|
|
|
}
|
|
|
|
|
// 在IE中释放内存
|
|
|
|
|
div = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 为指定的attrs添加相同的处理器
|
|
|
|
|
* @param {String} attrs 由管道分隔的属性列表
|
|
|
|
|
* @param {Function} handler 将要应用的方法
|
|
|
|
|
*/
|
|
|
|
|
function addHandle( attrs, handler ) {
|
|
|
|
|
var arr = attrs.split("|"),
|
|
|
|
|
i = arr.length;
|
|
|
|
|
// `assert`函数用于使用一个元素支持测试,它会创建一个`<div>`元素,然后将传入的函数`fn`应用到这个`<div>`元素上进行测试,根据函数执行情况返回相应的布尔值结果,同时在测试完成后会进行一些清理操作,比如从DOM中移除创建的`<div>`元素以及释放内存等(针对IE浏览器的特殊处理)。
|
|
|
|
|
// 参数`fn`是一个函数,这个函数会接收创建的`<div>`元素作为参数,并期望返回一个布尔结果,表示在这个元素上的某种测试情况(例如测试某个功能在该元素上是否可用等情况)。
|
|
|
|
|
function assert( fn ) {
|
|
|
|
|
// 创建一个`<div>`元素,这是后续用于测试的基础元素,通过`document.createElement`方法在当前文档中创建一个新的`<div>`标签对应的DOM元素,用于传递给`fn`函数进行相关测试操作。
|
|
|
|
|
var div = document.createElement("div");
|
|
|
|
|
|
|
|
|
|
while ( i-- ) {
|
|
|
|
|
Expr.attrHandle[ arr[i] ] = handler;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
// 调用传入的`fn`函数,并将创建的`div`元素作为参数传递进去,然后通过`!!`将函数返回的结果转换为布尔值(因为`fn`函数返回的结果可能不是严格的布尔类型,这样处理确保返回一个明确的布尔值,符合函数整体返回布尔结果的预期),并返回这个布尔值,表示测试的结果(如果`fn`函数执行过程中没有抛出异常且返回值在布尔判断中为真,则返回`true`,否则返回`false`)。
|
|
|
|
|
return!!fn( div );
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// 如果在调用`fn`函数的过程中抛出了异常,说明测试出现问题,直接返回`false`,表示测试不通过或者出现错误情况。
|
|
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
// 默认情况下从其父节点中移除创建的`div`元素,首先判断`div`元素是否有父节点(通过`div.parentNode`判断,如果存在父节点,说明该元素已经被添加到DOM树中了,需要将其移除),如果有父节点,则通过`div.parentNode.removeChild(div)`方法从DOM树中移除该`div`元素,这是为了避免创建的临时测试元素对DOM结构造成不必要的影响,保持DOM的整洁。
|
|
|
|
|
if ( div.parentNode ) {
|
|
|
|
|
div.parentNode.removeChild( div );
|
|
|
|
|
}
|
|
|
|
|
// 在IE中释放内存,通过将`div`设置为`null`,帮助浏览器的垃圾回收机制更好地回收创建`div`元素所占用的内存(在IE浏览器中,这种手动释放内存的操作有时候对于避免内存泄漏等问题有一定帮助,虽然现代浏览器在内存管理上已经比较智能,但这里做了这种兼容处理)。
|
|
|
|
|
div = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 检查两个兄弟节点的文档顺序
|
|
|
|
|
* @param {Element} a
|
|
|
|
|
* @param {Element} b
|
|
|
|
|
* @returns {Number} 如果a在b之前,则返回小于0的值;如果a在b之后,则返回大于0的值
|
|
|
|
|
*/
|
|
|
|
|
function siblingCheck( a, b ) {
|
|
|
|
|
var cur = b && a,
|
|
|
|
|
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
|
|
|
|
|
( ~b.sourceIndex || MAX_NEGATIVE ) -
|
|
|
|
|
( ~a.sourceIndex || MAX_NEGATIVE );
|
|
|
|
|
|
|
|
|
|
// 如果可用,则使用IE的sourceIndex
|
|
|
|
|
if ( diff ) {
|
|
|
|
|
return diff;
|
|
|
|
|
}
|
|
|
|
|
// `addHandle`函数用于为指定的属性(`attrs`)添加相同的处理器(`handler`),也就是将传入的处理函数`handler`关联到多个属性上,方便在后续处理这些属性相关逻辑时统一调用这个处理器进行操作。
|
|
|
|
|
// 参数`attrs`是一个由管道(`|`)分隔的属性列表,表示有多个属性需要关联同一个处理器,`handler`是将要应用的方法,也就是具体用于处理这些属性相关逻辑的函数。
|
|
|
|
|
function addHandle( attrs, handler ) {
|
|
|
|
|
// 将传入的以管道分隔的属性列表`attrs`通过`split("|")`方法进行分割,得到一个包含各个属性的数组`arr`,这样就把多个属性拆分开了,方便后续逐个进行处理器的关联操作。
|
|
|
|
|
var arr = attrs.split("|"),
|
|
|
|
|
i = arr.length;
|
|
|
|
|
|
|
|
|
|
// 检查b是否在a之后
|
|
|
|
|
if ( cur ) {
|
|
|
|
|
while ( (cur = cur.nextSibling) ) {
|
|
|
|
|
if ( cur === b ) {
|
|
|
|
|
return -1;
|
|
|
|
|
// 通过`while`循环,从后往前遍历属性数组`arr`(`i--`的方式实现从后往前遍历,这样在删除数组元素等操作时不会影响前面还未处理的元素索引情况),每次循环将当前属性(`arr[i]`)作为键,传入的`handler`函数作为值,存储到`Expr.attrHandle`对象中(`Expr`应该是外部定义的一个对象,`attrHandle`是它上面用于存储属性处理器关联关系的对象,通过这种方式建立了属性和处理器之间的对应关系,方便后续根据属性查找并调用对应的处理器)。
|
|
|
|
|
while ( i-- ) {
|
|
|
|
|
Expr.attrHandle[ arr[i] ] = handler;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return a ? 1 : -1;
|
|
|
|
|
}
|
|
|
|
|
// `siblingCheck`函数用于检查两个兄弟节点(`a`和`b`)的文档顺序,也就是判断在DOM树中这两个兄弟节点哪个在前面,哪个在后面,通过比较它们的相关属性或者遍历DOM结构来确定顺序关系,并返回相应的数值结果(小于0表示`a`在`b`之前,大于0表示`a`在`b`之后)。
|
|
|
|
|
// 参数`a`和`b`分别是要检查文档顺序的两个兄弟节点元素对象,这两个参数都应该是有效的DOM元素,并且是兄弟节点关系(即具有同一个父节点)。
|
|
|
|
|
function siblingCheck( a, b ) {
|
|
|
|
|
// 首先进行一些初始化和初步判断,将`b`的值赋给`cur`(如果`b`存在的话),同时将`cur`的值也赋给`a`(这里可能是为了后续方便统一的逻辑处理,先做这样的赋值操作,确保后续在比较和遍历过程中有统一的变量使用),这样`cur`变量在后续逻辑中可以作为临时的节点引用进行操作。
|
|
|
|
|
var cur = b && a,
|
|
|
|
|
// 计算两个兄弟节点的顺序差值,通过判断`a`和`b`的节点类型是否都是元素节点(`nodeType === 1`表示元素节点),如果都是元素节点,则尝试获取它们的`sourceIndex`属性(在某些浏览器中,特别是IE浏览器,`sourceIndex`属性可以用于表示元素在文档中的顺序索引,方便比较顺序,但不是所有浏览器都支持这个属性,所以需要做后续的兼容处理),将`b`的`sourceIndex`属性(如果不存在则使用`MAX_NEGATIVE`,前面代码中应该有其定义,是一个很大的负整数,用于处理不存在`sourceIndex`属性的情况)减去`a`的`sourceIndex`属性(同样如果不存在则使用`MAX_NEGATIVE`),得到一个差值`diff`,这个差值就初步表示了两个节点的顺序关系(如果`diff`小于0,说明`a`在`b`之前;如果`diff`大于0,说明`a`在`b`之后)。
|
|
|
|
|
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
|
|
|
|
|
( ~b.sourceIndex || MAX_NEGATIVE ) -
|
|
|
|
|
( ~a.sourceIndex || MAX_NEGATIVE );
|
|
|
|
|
|
|
|
|
|
// 如果计算得到的差值`diff`存在(即不为`0`或者`undefined`等情况),说明可以通过`sourceIndex`属性判断出两个节点的顺序关系,直接返回这个差值`diff`,函数结束(这里利用了`sourceIndex`属性可以直接比较顺序的便利性,在支持该属性的浏览器中能快速得到结果)。
|
|
|
|
|
if ( diff ) {
|
|
|
|
|
return diff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果不能通过`sourceIndex`属性判断顺序(即`diff`不存在或者为0等情况),则进入以下通过遍历DOM结构来判断顺序的逻辑。
|
|
|
|
|
// 首先检查`cur`是否存在(也就是`b`是否存在,因为前面`cur`的赋值和`b`相关),如果`cur`存在,则通过`while`循环,从`cur`(也就是`b`)开始,不断查找它的下一个兄弟节点(通过`cur = cur.nextSibling`操作,每次循环将`cur`更新为它的下一个兄弟节点),只要能找到下一个兄弟节点就继续循环查找。
|
|
|
|
|
if ( cur ) {
|
|
|
|
|
while ( (cur = cur.nextSibling) ) {
|
|
|
|
|
// 在遍历过程中,如果找到了和`a`相等的节点(即`cur === b`),说明`b`在`a`之后,返回 -1,表示`a`在`b`之前,函数结束(这里通过实际的DOM结构遍历找到了两个节点的顺序关系)。
|
|
|
|
|
if ( cur === b ) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果遍历完`b`之后的所有兄弟节点都没有找到`a`,说明`a`不在`b`之后,那么`a`要么是`b`之前的兄弟节点,要么`b`不存在(前面`cur`的赋值和`b`相关,如果`b`不存在也会走到这一步),返回1,表示`a`在`b`之后(这里涵盖了`b`不存在或者`a`在`b`之前的所有情况,通过返回1统一表示`a`在`b`之后的顺序关系),函数结束。
|
|
|
|
|
return a? 1 : -1;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Returns a function to use in pseudos for input types
|
|
|
|
|
* @param // 创建一个函数,用于生成一个判断元素是否为特定类型input的函数
|
|
|
|
|