针对 NodeList 调用 Array.prototype.slice 的目的是啥? [复制]
Posted
技术标签:
【中文标题】针对 NodeList 调用 Array.prototype.slice 的目的是啥? [复制]【英文标题】:What is the purpose of calling Array.prototype.slice against a NodeList? [duplicate]针对 NodeList 调用 Array.prototype.slice 的目的是什么? [复制] 【发布时间】:2014-03-22 04:35:46 【问题描述】:我在查找如何迭代 NodeLists 时遇到了以下代码。
var nodesArray = Array.prototype.slice.call(nodeList);
nodesArray.forEach(function(node)
//...
)
针对 NodeList 调用 Array.prototype.slice
的目的是什么?
【问题讨论】:
我觉得有时解决重复关闭争论的方法是 MOAR 重复关闭。发现这个问题的人们往往想知道两件事:1)这个奇怪的原型..call()模式是怎么回事? (经典的 DOM API 怪异)和 2)为什么我需要将它与 NodeList 一起使用(你不需要)。这两个问题之前(很多)都被问过和回答过 - 所以让我们链接到他们,嗯? @shog9 所以换句话说我最初的欺骗投票完全没问题? @JonasWilms 是的,我知道,我们把他送到了这里 :) 我很高兴它已经排序。如果您有任何疑虑,请不要犹豫,在帖子上竖起自定义标记。 【参考方案1】:因为slice
将任何类似数组的参数的副本作为新的数组对象返回,这正是我们所需要的。我们可以很容易地使用concat
。
【讨论】:
您确定concat
会起作用吗?我原以为这是一种在这种情况下可能会失败的数组方法?我想看看怎么做。 jsfiddle.net/Xotic750/k552C
你不能单独使用concat,你必须slice the array first,这会给你和单独使用slice一样的效果。【参考方案2】:
使用 forEach 方法迭代一个 NodeList
但我不明白为什么我们使用切片方法?
你不必,你可以直接这样做
Array.prototype.forEach.call(nodelist, function(value, index)
...
);
【讨论】:
【参考方案3】:针对 NodeList 调用
Array.prototype.slice
的目的是什么?
Array#slice
方法“将数组的一部分的浅拷贝返回到新的数组对象中”。
Function#call
方法“使用给定的 this 值和单独提供的参数调用函数”。
由于数组是对象,所有对象属性名称都存储为字符串,并且所有节点列表都使用顺序编号的属性名称存储其元素(再次存储为字符串),节点列表可以用作数组方法的this
值。
将 NodeList 的浅表副本创建为 Array 允许您在新创建的 Array 上使用其他 Array 方法,而无需使用 Function#call
。
许多现代浏览器都实现了NodeList#forEach
,尽管该方法仍是候选推荐方法,目前尚未将其纳入规范。我建议谨慎使用该方法,不要在开放的网络上使用,直到它达到更稳定的状态。
以下是使用 NodeList 作为目标调用 Array 方法的一些其他示例:
// Convert to an array, then iterate
const nodeArray = Array.prototype.slice.call(nodeList)
nodeArray.forEach(doSomething);
// Iterate NodeList directly without conversion
Array.prototype.forEach.call(nodeList, doSomething);
// Perform operation on each element in NodeList, output results to a new Array
Array.prototype.map.call(nodeList, function(item)
return item;
).forEach(doSomething);
// Filter NodeList, output result to a new Array
Array.prototype.filter.call(nodeList, function(item)
return /* condition */;
).forEach(doSomething);
还有许多其他方法可以迭代 NodeList,而不需要使用 Array 方法,这里有更多示例:
您可以使用老式的 for 循环,从零开始循环,直到到达数组的末尾。这种方法一直存在,今天仍然经常使用。与此处提到的其他方法相比,这种方法的可读性稍差一些,但这一切都归结为您更容易编写的方法。
for(let i = 0; i < nodeList.length; ++i) doSomething(nodeList[i]);
由于lack of conditional evaluation,使用向后循环(在可能的情况下)可以节省执行时间。事实上,有些 IDE 默认会将前面的循环转换为下面的结构。
for(let i = nodeList.length; i--;) doSomething(nodeList[i]);
您可以使用 while 循环,它需要一个条件语句作为其参数。如果NodeList.item(n)
超出了 NodeList 的边界,它将返回 null,这将结束循环。
let i = 0, node;
while((node = nodeList.item(i++))) doSomething(node);
您可以在条件中使用 for 循环来做同样的事情:
let node;
for(let i = 0; (node = nodeList.item(i)); i++) doSomething(node);
您可以使用带有Object.keys()
的for...in 循环。请注意,在使用 for...in 循环时必须使用Object.keys
,否则它将遍历不可枚举的属性以及可枚举的属性。
Object.keys() 方法返回给定对象自己的可枚举属性的数组,其顺序与 for...in 循环提供的顺序相同(不同之处在于 for-in 循环枚举原型链)。来自: https://developer.mozilla.org/en-US/docs/Web/javascript/Reference/Global_Objects/Object/keys
for(var i in Object.keys(nodeList)) doSomething(nodeList[i]);
您可以通过从 Array() 检索 Iterator 函数并将其应用于 NodeList 来使用 for...of
循环 (ECMAScript 2015+)。只要属性是可枚举的,这也适用于对象的大多数其他用途。
nodeList[Symbol.iterator] = [][Symbol.iterator];
for(node of nodeList) doSomething(node);
如果将数组迭代器应用于 NodeList 类的原型,那么无论何时创建 NodeList 的新实例,它都将始终是可迭代的。但是,不建议扩展原生原型。
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
for(node of nodeList) doSomething(node);
【讨论】:
【参考方案4】:所有这些答案都已过时。实际上你可以在现代浏览器的 NodeList 上使用forEach
!
所以只需使用forEach!
【讨论】:
以上是关于针对 NodeList 调用 Array.prototype.slice 的目的是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章