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

4

9

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?

Evidentiary answered 20/2, 2014 at 22:40 Comment(3)
I feel like sometimes the solution to arguments over dup-closing is MOAR dup-closing. Folks finding this question are going to tend to want to know two things: 1) what's the deal with this weird prototype..call() pattern? (classic DOM API weirdness) and 2) why do I need to use it with NodeList (you don't). Both of those questions have been asked & answered before (a lot) - so let's just link to 'em, eh?Outfield
@shog9 so in other words my initial dupe vote was totally fine?Isochromatic
@JonasWilms yes I know, we sent him here :) I'm glad it's sorted. Please don't hesitate to raise a custom flag on posts when you have concerns.Gudren
S
17

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);
Simonne answered 21/5, 2015 at 23:58 Comment(0)
A
3

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) {
    ...
});
Apollinaire answered 20/2, 2014 at 22:58 Comment(0)
A
1

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

So just use forEach!

Agog answered 9/4, 2018 at 13:38 Comment(0)
W
0

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.

Weise answered 20/2, 2014 at 22:42 Comment(2)
Are you sure that concat would work, I would have thought that was one array method that would likely fail in this situation? I would like to see how to. jsfiddle.net/Xotic750/k552CApollinaire
You cannot use concat by itself, you would have to slice the array first, which would give you the same effect as just using slice by itself.Simonne

© 2022 - 2024 — McMap. All rights reserved.