Explain Array.prototype.map.call

I came across some code that splits a string into an array in the same way you would with something like the split() method, but instead it uses Array.prototype.map.call, and I don’t understand how it does so. Can someone please explain what is happening with this function?

function splitter(string) {
  return Array.prototype.map.call(string, (x) => x);
}

console.log(splitter('Hello World'));

The result is

["H", "e", "l", "l", "o", " ", "W", "o", "r", "l", "d"]

Hey @tsjohns9,
Array is an object in Javascript and prototype is a property of the Array object.
The prototype property allows you to add new functionality to the array object, so that any Array object will have them.

What the line you mentioned does, is access the map function which has the following signature :

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
    // Return element for new_array
}[, thisArg])

The call at the end of the line is used to call the anonymous function,

(x) => x)

since map expects a function to be used for each element in the array.

I hope that helps clear things a bit.

You can read more about it here:

Thanks, the part that is confusing me is the .map.call part. I am not sure how we are passing a string into .map and converting it to an array. My understanding of .call is that it sets the ‘this’ value of whatever you determine, so it looks like we are saying that the ‘this’ value is that of string, and then we are calling the function (x) => x. Correct me if I am wrong. How is .call making it so that we can pass a string into map and get an array?

map always returns an array, regardless of calling it directly like you do with regular arrays, or calling with call.

What is it about .call that will result in the string getting split into an array, because when I try to run Array.prototype.map(string, (x) => x)); It doesn’t work.

That’s a regular map call, with Array.prototype as the this value (and being a regular call, it expects the first argument to be a function, not a string).

Using .call allows you to explicitly set the this value for .map (the first argument). It’s as if you’re doing string.map((x) => x);. It’s kind of like the string borrowing the array’s map function.

2 Likes

So we are setting the this value of .map to string instead of its default this value of Array.prototype, and executing map on the string as if it were an array. How does this work though since string is still not an array?

1 Like

I’m not sure, but I guess that the map function assumes that the this value is an object that has a length property and integers as properties (when accessing specific chars from a string you use this; like str[3] etc.), which strings have. If the this value have this properties, the map function just works as if this is an array.

Strings are iterable, same as arrays, so you can just borrow the array methods that iterate over a collection for certain things (ie what youre borrowing has to make logical sense, so in this case map applies a function to every element, and in this case every element is a character in a string). The way it’s being used is a bit tricky: split is more obvious and probably more performant.

Strings implementing the iterable protocol means you can also do:

function splitString(str) {
  return [...str];
}

Or

function splitString(str) {
  const charList = [];
  for (const char of str) charList.push(char);
  return charList;
}
// assume we create a prototype function on array object
Array.prototype.simpleIteration = function(callback) {
	let newArray = []
	for (let x = 0; x < this.length; x++) {
		newArray.push( callback(this[x]) )
	}
	return newArray
}

// and now we create a new sample array
let sampleArray = ["a","b","c"]

// we can call this by
let result1 = sampleArray.simpleIteration( x => x+x )
console.log("result1:")
console.log(result1) // result1 becomes "aa","bb","cc"

// or we can call it by
let result2 = Array.prototype.simpleIteration.call(sampleArray, x => x+x)
console.log("result2:")
console.log(result2) // result2 becomes "aa","bb","cc"

// So lets say we created a string
let sampleString = "abc"
// since simpleIteration function were attached to Array functions
// if we create a string and call simple Iteration wouldn't work

/* this code wouldn't work
let result3 = sampleString.simpleIteration(x=>x+x)
console.log("result3:")
console.log(result3)
*/

// however, to get around it .. we can use the call method
let result4 = Array.prototype.simpleIteration.call(sampleString, x => x+x)
console.log("result4")
console.log(result4)


as we can see … the function would work just as fine when it operates on a string.
it is only just because it was attached to an Array.prototype that makes us not to be able to call it directly from a string, so we get around it by using call.

hope the sample code above helps
cheers

4 Likes

.call expects an object as its first parameter then uses the function that precedes itself.
run code to see string turn into an object (or object-like string)

function splitter() {
  return this
}
console.dir(splitter.call('Hello World'))

we passed a string instead of an object as a parameter but still returned an object. why? because js. anyways, now that we have a set of values, we then borrow an array method using Array.prototype to map our object. using .map callback function to iterate our object wherein x refers to the index of our object.

thanks for sharing this neat code (:

1 Like

Totally get it now, thanks everyone