针对 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 的目的是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

类数组转数组Array.prototype.slice.call(arrayLike)

在go语言中,如何在反引号中调用变量的值而不是变量名

javaScript之NodeList

NodeList

NodeList对象的特点

为什么 NodeList 不是数组?