Nowadays, we can convert a NodeList into a true array by using the modern Array.from() method. But this requires a polyfill for IE, so I still use the older Array.prototype.slice.call() method sometimes. It's a lot harder to read, though, so let's dig into how it works!

The slice() method

Normally, you call the slice() method directly on an array instance. It copies a segment of an array into a brand new array:

var fruits = ["Apples", "Bananas", "Cherries"];

var bananasAndCherries = fruits.slice(1);

You can also create a complete copy of an array by calling the slice() method without any arguments:

var fruits = ["Apples", "Bananas", "Cherries"];

var fruitsCopy = fruits.slice();

This is the important part.

All array instances inherit the slice() method through their prototype chain. Don't worry too much about this technical term, but if you're interested, you can learn more in the MDN Web Docs:

Learn more: Inheritance and the prototype chain.

The call() method

The call() method is available to all functions via their prototype chain.

As explained in the MDN Web Docs:

The call() method calls a function with a given this value and arguments provided individually.

Essentially, binding a different value for this changes the scope of the function.

Changing the scope lets you call the function on an object to which it would not normally be available.

Putting it all together

Let's look at the following example:

// Get all paragraphs as a NodeList
var paragraphs = document.querySelectorAll("p");

// Convert the NodeList into an array
paragraphs = Array.prototype.slice.call(paragraphs);

First, we get all paragraphs on the page using the querySelectorAll() method.

This returns a NodeList, though.

As I mentioned above, the slice() method is only available to array instances. Our NodeList cannot access it directly.

So we can't just call paragraphs.slice() to create a copy of the NodeList as a brand new array. We'd get a TypeError if we tried:

TypeError: paragraphs.slice is not a function

Remember that the call() method allows us to change the scope of a function, so we can call it on an object to which it would not normally be available?

Normally, when you call the slice() method directly on an array instance, its internal this value points to that array instance.

But here, we change the this value so that it points to the NodeList we have stored in the paragraphs variable:

paragraphs = Array.prototype.slice.call(paragraphs);

This successfully creates a brand new copy of the NodeList as an array, so all the array instance methods are now available to us!

Further reading

The this keyword and the Function.prototype.call() method can be really confusing. Even seasoned developers struggle with them.

If you want to learn more, I recommend the following:

If none of this makes sense, seriously, don't worry about it. You can still use the Array.prototype.slice.call() trick safely.

I've been working with JavaScript for several years now, and I've only just started to feel more comfortable with these concepts.