Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

forEach() vs Array.prototype.forEach.call()

Tags:

I was just goin through the Electron API Demo code samples when suddenly a wild expression - which is completely foreign to me - appeared:

const links = document.querySelectorAll('a[href]');  Array.prototype.forEach.call(links, function (link) {     // WWIII here }) 

I definitely understand what this piece of code is doing but I am used to a syntax like this:

links.forEach(function (links) {}); 

So what exactly is the difference between those two? I have already read various StackOverflow threads about this topic but they are either ambiguous or don't answer the question at all. Some said it had something todo with array-like collections not being iteratable by .forEach() as opposed to Array.prototype.forEach.call(). Is that the only advantage of the overly tedious and long version?

Thanks in advance!

like image 738
Niklas Vest Avatar asked May 02 '17 17:05

Niklas Vest


People also ask

What is the difference between the map and forEach method on the array prototype?

The first difference between map() and forEach() is the returning value. The forEach() method returns undefined and map() returns a new array with the transformed elements. Even if they do the same job, the returning value remains different.

What is one difference between the array forEach () and the array map () methods in JavaScript?

Differences between forEach() and map() methods:The forEach() method does not create a new array based on the given array. The map() method creates an entirely new array. The forEach() method returns “undefined“. The map() method returns the newly created array according to the provided callback function.

Is forEach a callback function?

forEach() calls a provided callbackFn function once for each element in an array in ascending index order.

What is difference between map () and forEach ()?

One of the main differences between forEach() and map() methods is their ability to chain other methods. map() is chainable but forEach isn't. This means that one could use reduce(), sort(), and other methods after map() but that's not possible with foreach() because it returns undefined.


2 Answers

This is interesting question. Half a year ago I would say that link.forEach is not about shorter syntax, but it is actually not supposed to work. Then I would explain what it means that many of array methods deliberately generic, which means that their internal implementation only considers numeric indexes and length property of the this object, but doesn't care about it being Array instance. Basically what @Pedro Castilho said in his answer.

However, now I will say that these days evergreen browsers (except IE11, Edge, as of April 2017) already implemented NodeList.prototype.forEach convenience method so you no longer need to use .call hack or Array.from in order to just iterate NodeList with forEach.

So my summary: if you don't need to support IE, then use NodeList.prototype.forEach rather than Array.prototype.forEach. It might be the same internally, but cleaner conceptually. If you do need to support IE and you don't want to include one more pollyfill then use Array.prototype.call or better Array.from.

like image 33
dfsq Avatar answered Oct 26 '22 15:10

dfsq


"class methods" in JavaScript are actually functions defined on a prototype. That means that even if an object does not inherit from the Array prototype, you can call Array methods on it, as long as it follows the array structure (i.e.: It is an object with a length property and properties indexed by integers). However, the object holds no reference to Array.prototype, so you need to explicitly select Array.prototype as the object the method lives in.

The document.querySelectorAll function returns a NodeList, which is neither an Array nor inherits from the Array prototype. However, as a NodeList has a similar internal structure to an Array, you can still use the forEach function. But since NodeList does not inherit from the Array prototype, trying to use .forEach on a NodeList would raise an error (this is not exactly true - see the note at the end of my answer). For this reason, you need to explicitly state that you are calling a method from Array.prototype on the NodeList, and that is done by using the .call method from Function.prototype.

In summary:

Array.prototype.forEach.call(links, function(link) { /* something */ }) 

means:

Take the forEach function from Array.prototype and call it on links, which is a non-Array object, with some function as its argument.

Note that on recent versions of browsers, the NodeList prototype does provide a forEach method which works the same as the Array one, so the example from the Electron API is probably using the Array version for compatibility with older versions. If you have a web app and only care about supporting modern versions of Chrome and Firefox, you can just call forEach on your NodeList. In fact, since Electron updates about 2 weeks after whenever Chrome updates, it should be safe to use NodeList.prototype.forEach in Electron. :)

like image 83
Pedro Castilho Avatar answered Oct 26 '22 15:10

Pedro Castilho