Xun Yang
Xun Yang

Reputation: 4419

Array.prototype.map.call vs Array.map

I'm reading about querySelector method, the article suggests that one for example shall use Array.prototype.map.call() instead of map(). I have no problem with that, however I don't really see why Array.prototype.map.call() should be chosen over Array.map() What makes one better than the other?

let nodes = document.querySelectorAll('div.menu-item')

Array.prototype.map.call(nodes, one => one.innerHTML)
//vs
Array.map(nodes, one => one.innerHTML)

Upvotes: 2

Views: 1464

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1074355

What makes one better than the other?

There is no Array.map in the standard JavaScript library. So if you use it, you're relying on an unspecified feature that may or may not be present in all of your target environments. (Firefox's SpiderMonkey JavaScript engine provides it, for instance; Chrome's V8 engine does not, as of this writing.) So that would be a fairly substantial reason to prefer Array.prototype.map.call — of those two choices. :-)

But, as ste2425 points out, given your use of let and arrow functions, you appear to be coding for an ES2015+ environment. In that case (or if you add a polyfill), you can use the mapping features of Array.from:

let innerHTMLArray = Array.from(nodes, o‌​ne => one.innerHTML);

Array.from creates an array from any array-like object or iterable object. The NodeList from querySelectorAll is both. :-) And it optionally lets you map the values as it's doing that.

Example:

if (!Array.from) {
  console.error("Your browser doesn't support Array.from yet.");
} else {
  const htmlArray = Array.from(document.querySelectorAll("div.menu-item"), e => e.innerHTML);
  console.log(htmlArray);
}
<div>I'm not included in the result because I don't have the class</div>
<div class="menu-item">Menu item #1</div>
<div class="menu-item">Menu item #2</div>
<div class="menu-item">Menu item #3</div>

Upvotes: 2

SVSchmidt
SVSchmidt

Reputation: 6527

First, there is no Array.map. The use case for the former variant is the following: You won't be able to invoke map for example via document.querySelectorAll('div')[0].map. That is to the fact, that document.querySelectorAll returns a NodeList and not an array and lacks the map method.

What makes one better than the other?

The question really is not which variant is better. It is about not being able to call map on the NodeList.

Most functions within the array prototype are able to handle array-like objects as well (that is, objects whose keys are representations of numeric indices) when passed as thisArg to call(or apply). Hence,Array.prototype.map.call(array-like, mapFunction)` can map the NodeList just like an array.

There's two alternatives, however:

  1. Use Array.from: Array.from(nodes, n => n.innerHTML) introduced in ES6 and not supported in Internet Explorer. However, when that does not matter, I would prefer this.
  2. Use Array.prototype.slice.call(nodes).map(n => n.innerHTML), which works similar (see how does Array.prototype.slice.call() work?) or stick with Array.prototype.map.call.

According to your overall code, however, the usage of (1) seems perfectly fine and I, personaly, would prefer this.

Upvotes: 3

Related Questions