What is the purpose of calling Array.prototype.slice against a NodeList? [duplicate]

岁酱吖の 提交于 2019-11-27 18:31:25

问题


This question already has an answer here:

  • Explanation of [].slice.call in javascript? 7 answers
  • how does Array.prototype.slice.call() work? 13 answers
  • Why doesn't nodelist have forEach? 10 answers

I was looking up how to iterate NodeLists and I came across the following bit of code.

var nodesArray = Array.prototype.slice.call(nodeList);
nodesArray.forEach(function(node) { 
    //...
})

What is the purpose of calling Array.prototype.slice against a NodeList?


回答1:


What is the purpose of calling Array.prototype.slice against a NodeList?

The Array#slice method "returns a shallow copy of a portion of an array into a new array object".

The Function#call method "calls a function with a given this value and arguments provided individually".

Because Arrays are Objects, all Object property names are stored as strings, and all NodeLists store their elements with sequential numbered property names (again stored as strings), NodeLists can be used as the this value for Array methods.

Creating a shallow copy of the NodeList as an Array allows you to use other Array methods on the newly created Array without using Function#call.

Many modern browsers have implemented NodeList#forEach, though the method is still a candidate recommendation and has not made it into the specification at this point. I recommend using that method with care, and not on the open web, until it has reached a more stable status.


Here are some other examples of Array methods being called with a NodeList as the target:

// 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);

There are many other ways that you can iterate a NodeList that don't require the use of Array methods, here are some more examples:

You can use a good old fashioned for loop, start at zero and loop until we reach the end of the array. This method has been around for ever and is still used regularly today. This method is, somewhat, less readable than other methods mentioned here, but it all comes down to what is easier for you to write.

for(let i = 0; i < nodeList.length; ++i)  doSomething(nodeList[i]);

Using a backwards loop (where possible) can save reduce execution time due to a lack of conditional evaluation. In fact, some IDE's will convert the previous loop to the following structure by default.

for(let i = nodeList.length; i--;)  doSomething(nodeList[i]);

You can use a while loop, which expects a conditional statement as its parameter. If NodeList.item(n) is past the bounds of the NodeList it will return null, which will end the loop.

let i = 0, node;
while((node = nodeList.item(i++))) doSomething(node);

You can do the same thing with a for loop in the conditional:

let node;
for(let i = 0; (node = nodeList.item(i)); i++) doSomething(node);

You can use a for...in loop with Object.keys(). Note that you have to use Object.keys when using a for...in loop because otherwise it will iterate over the non-enumerable properties as well as the enumerable ones.

The Object.keys() method returns an array of a given object's own enumerable properties, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
From: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

for(var i in Object.keys(nodeList))  doSomething(nodeList[i]);

You can use a for...of loop (ECMAScript 2015+) by retrieving the Iterator function from Array() and applying it to the NodeList. This will work for most other uses of an object as well, as long as the properties are enumerable.

nodeList[Symbol.iterator] = [][Symbol.iterator];
for(node of nodeList) doSomething(node);

If you apply the Array Iterator to the prototype of the NodeList class then whenever a new instance of NodeList is created, it will always be iterable. However, it is not advisable to extended native prototypes.

NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
for(node of nodeList) doSomething(node);



回答2:


Iterate a NodeList using forEach method

But I don't understand why we used the slice method ?

You don't have to, you could do this directly

Array.prototype.forEach.call(nodelist, function(value, index) {
    ...
});



回答3:


Because slice returns a copy of any array-like argument as a new array object, which is exactly what we need. We could just as easily use concat.




回答4:


All these answers are outdated. Actually you can use forEach on NodeList in modern browsers!

So just use forEach!



来源:https://stackoverflow.com/questions/21921282/what-is-the-purpose-of-calling-array-prototype-slice-against-a-nodelist

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!