如何测试两个 NodeLists 的相等性?
How can I test the equality of two NodeLists?
假设我有一个自定义函数,我希望它会 return 一个 NodeList:
getNodeList('foo');
我希望这个 NodeList 与 return 的 NodeList 相同:
document.querySelectorAll('.foo');
如何检查我的期望是否正确?
这样做不起作用:
getNodeList('foo') == document.querySelectorAll('.foo')
我确定这是行不通的技术原因,因为 document.querySelectorAll('.foo') == document.querySelectorAll('.foo')
也行不通,我认为这是预料之中的。
如何测试两个 NodeList 是否包含相同的 HTML 个节点?
我想出了这个似乎有效的方法,使用 ES6 特性:
const isEqual = [...getNodeList('foo')].every((node, index) => {
return Array.from(document.querySelectorAll('.foo')).indexOf(node) === index
});
本质上,我们测试第一个 NodeList 中的每个项目都存在于第二个 NodeList 的相同索引处。如果 NodeList 之间存在任何差异,这应该 return false。
数组相等是通过引用,而不是内容。
let a = [1, 2, 3], b = [1, 2, 3]
let c = a
a == c // => true, since both refer to `a`
a == b // => false
如果你想比较两个类似数组的对象,你必须按索引比较。
function eq(A, B) {
if (A.length !== B.length) return false;
for (let i = 0; i < A.length; i++) {
if (A[i] !== B[i]) return false;
}
return true;
}
当然你总是可以使用一些函数式编程魔法:
let arrayEq = (A, B) => A.length === B.length && A.every((e, i) => e === B[i]);
但只有当 A 是数组(而不是 NodeList)时才有效。
那就试试
eq(getNodeList('foo'), document.querySelectorAll('.foo'))
或
arrayEq(Array.from(getNodeList('foo')), Array.from(document.querySelectorAll('.foo'))
到目前为止,您所做的似乎还不错,但效率很低(您可能会多次重新计算 document.querySelectorAll(...)
和 indexOf
)。
还有一个错误:如果 querySelectorAll
return 的元素比第一个节点列表多,但它们是相等的,你的函数将 return true
.
您还可以进一步简化比较:
function nodeListsAreEqual( list1, list2 ) {
if ( list1.length !== list2.length ) {
return false;
}
return Array.from( list1 ).every( ( node, index ) => node === list2[ index ] );
}
如果您不介意安装第三方库,请从 NPM 获取 deep-equal
并执行:
deepEqual(Array.from(getNodeList('foo')), Array.from(document.querySelectorAll('.foo')))
这确保您的列表只计算一次,并将列表比较的所有细节封装到一个单独的函数中。您的代码应该简单地调用一个相等函数,而不是将您的应用程序关注点与列表结构的低级遍历混为一谈。 (但你可能已经知道了!)
如果您不喜欢 Array.from
的冗长,请使用 splats:
deepEqual([...getNodeList('foo')], [...document.querySelectorAll('.foo')])
如果效率很重要,您需要进行一些分析。
在需要比较可能不共享相同节点顺序但节点相等的 NodeList 之后,我重新访问了这个,并提出了这个:
export default function nodeListsAreEqual(actual, expected) {
if ((actual.length || Object.keys(actual).length) !== (expected.length || Object.keys(expected).length)) {
return false;
}
return Array.from(actual).every(actualNode => {
return Array.from(expected).some(expectedNode => actualNode === expectedNode)
});
}
一个更简单但可能更快的@mauroc8 版本:
const nodeEq = (A, B) => {
A = Array.from(A)
B = Array.from(B)
if (A.length !== B.length) return false
return A.every((a, b) => a.innerHTML === B[b].innerHTML)
}
假设我有一个自定义函数,我希望它会 return 一个 NodeList:
getNodeList('foo');
我希望这个 NodeList 与 return 的 NodeList 相同:
document.querySelectorAll('.foo');
如何检查我的期望是否正确?
这样做不起作用:
getNodeList('foo') == document.querySelectorAll('.foo')
我确定这是行不通的技术原因,因为 document.querySelectorAll('.foo') == document.querySelectorAll('.foo')
也行不通,我认为这是预料之中的。
如何测试两个 NodeList 是否包含相同的 HTML 个节点?
我想出了这个似乎有效的方法,使用 ES6 特性:
const isEqual = [...getNodeList('foo')].every((node, index) => {
return Array.from(document.querySelectorAll('.foo')).indexOf(node) === index
});
本质上,我们测试第一个 NodeList 中的每个项目都存在于第二个 NodeList 的相同索引处。如果 NodeList 之间存在任何差异,这应该 return false。
数组相等是通过引用,而不是内容。
let a = [1, 2, 3], b = [1, 2, 3]
let c = a
a == c // => true, since both refer to `a`
a == b // => false
如果你想比较两个类似数组的对象,你必须按索引比较。
function eq(A, B) {
if (A.length !== B.length) return false;
for (let i = 0; i < A.length; i++) {
if (A[i] !== B[i]) return false;
}
return true;
}
当然你总是可以使用一些函数式编程魔法:
let arrayEq = (A, B) => A.length === B.length && A.every((e, i) => e === B[i]);
但只有当 A 是数组(而不是 NodeList)时才有效。
那就试试
eq(getNodeList('foo'), document.querySelectorAll('.foo'))
或
arrayEq(Array.from(getNodeList('foo')), Array.from(document.querySelectorAll('.foo'))
到目前为止,您所做的似乎还不错,但效率很低(您可能会多次重新计算 document.querySelectorAll(...)
和 indexOf
)。
还有一个错误:如果 querySelectorAll
return 的元素比第一个节点列表多,但它们是相等的,你的函数将 return true
.
您还可以进一步简化比较:
function nodeListsAreEqual( list1, list2 ) {
if ( list1.length !== list2.length ) {
return false;
}
return Array.from( list1 ).every( ( node, index ) => node === list2[ index ] );
}
如果您不介意安装第三方库,请从 NPM 获取 deep-equal
并执行:
deepEqual(Array.from(getNodeList('foo')), Array.from(document.querySelectorAll('.foo')))
这确保您的列表只计算一次,并将列表比较的所有细节封装到一个单独的函数中。您的代码应该简单地调用一个相等函数,而不是将您的应用程序关注点与列表结构的低级遍历混为一谈。 (但你可能已经知道了!)
如果您不喜欢 Array.from
的冗长,请使用 splats:
deepEqual([...getNodeList('foo')], [...document.querySelectorAll('.foo')])
如果效率很重要,您需要进行一些分析。
在需要比较可能不共享相同节点顺序但节点相等的 NodeList 之后,我重新访问了这个,并提出了这个:
export default function nodeListsAreEqual(actual, expected) {
if ((actual.length || Object.keys(actual).length) !== (expected.length || Object.keys(expected).length)) {
return false;
}
return Array.from(actual).every(actualNode => {
return Array.from(expected).some(expectedNode => actualNode === expectedNode)
});
}
一个更简单但可能更快的@mauroc8 版本:
const nodeEq = (A, B) => {
A = Array.from(A)
B = Array.from(B)
if (A.length !== B.length) return false
return A.every((a, b) => a.innerHTML === B[b].innerHTML)
}