Search code examples
javascriptarraysdictionarythiscall

How does 'this' keyword work in map() and call()?


I have been refreshing my JavaScript knowledge of call() and map() usage on NodeList.

It was fairly easy to google out, what call() should be doing and there are resources with examples of how it works with map().

However, as I noticed on MDN, the map() function can also take a second argument, which should be setting the this keyword for map() - or at least that is what I think it should be doing.

I have tried to check it myself with simple arrays:

var numbers = [1, 2, 3];
var letters = ['a', 'b', 'c'];

..and with a simple function, that is about to be given as a parameter to map():

var effector = function (x) {
    console.log(x);
}

Now, what I do not understand, is why these two function calls have different results:

numbers.map(effector, letters);
numbers.map.call(letters, effector);

I expect them to both output letters to the console, as both this keywords should be referencing to them (respectively to their objects).

I did some further research, and tried with this modified effector function:

var effector = function () {
    console.log(this);
}

..again on:

numbers.map(effector, letters);
numbers.map.call(letters, effector);

Assuming, that we are in "use strict", the first call logs letters and the second logs undefined.

But again, I would expect both these calls to produce the same output.

What am I missing?

EDIT:

I was reading, how .map can be polyfilled, if you check it on MDN, there you see, how the second parameter of .map is used in callback.call().

I suppose, that in the end, even that callback.call() should have the same this as was in .map.

MDN - map


Solution

  • There are two bindings of the this reference at play here:

    • this for the execution context of the map function
    • this for the execution context of the callback function

    They don't relate to each other.

    The second argument of map dictates what this will be for the callback. If it is not provided, the default is undefined (not the array).

    The first argument of map.call dictates what this will be for map -- and by consequence which array will be iterated.

    This is also reflected in the polyfill provided on mdn: it is perfectly in line with these specifications: O gets the value of this for the map function, and T gets the value of this for the callback. They are generally different.

    map versus forEach

    Unrelated to your question, but worth mentioning: don't use map when you are not actually mapping anything. map is intended for creating a new array, one in which every value has been mapped by calling the callback function on the original value at that same index.

    When however you just need to iterate the array values, without any intent to perform such mapping, then use the forEach method, or a for...of loop. Both these work on NodeList out of the box, without the need to .call.