Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the right "this" in an Array.map?

Tags:

javascript

I assume there is some application of call or apply here but I'm not sure how to implement it.

http://codepen.io/anon/pen/oXmmzo

a = {
  foo: 'bar',
  things: [1, 2, 3],
  showFooForEach: function() {
    this.things.map(function(thing) {
      console.log(this.foo, thing);
    });
  }
}

a.showFooForEach();

Say I want to map an array, but in the function, I need access to the this to which foo belongs. The function of map creates a new this context, so I obviously need to coerse that context back in somehow, but how do I do that while still having access to thing?

like image 811
Adam Grant Avatar asked Aug 06 '15 21:08

Adam Grant


People also ask

How do you add a condition to an array map?

To use a condition inside map() in React: Call the map() method on an array. Use a ternary operator to check if the condition is truthy. The operator returns the value to the left of the colon if the condition is truthy, otherwise the value to the right is returned.

How do you use an array map?

Definition and Usage map() creates a new array from calling a function for every array element. map() calls a function once for each element in an array. map() does not execute the function for empty elements. map() does not change the original array.


5 Answers

Just realized I should have read the documentation for Array.map() more carefully. One simply needs to pass in the this value as the second parameter of map()

http://codepen.io/anon/pen/VLggpX

a = {
  foo: 'bar',
  things: [1, 2, 3],
  showFooForEach: function() {
    this.things.map(function(thing) {
      console.log(this.foo, thing);
    }, this);
  }
}
a.showFooForEach();

In addition, understanding how bind(), call() and apply() work are a must for serious JavaScript developers. These allow us to skip silly assignments like

var self = this;
myItems.map(function(item) {
  self.itemArray.push(item);
});

with

myItems.map(function(item) {
  this.itemArray.push(item);
}.bind(this));
like image 156
Adam Grant Avatar answered Sep 25 '22 04:09

Adam Grant


As of 2018, you could use an arrow function:

a = {
  foo: 'bar',
  things: [1, 2, 3],
  showFooForEach: function() {
    this.things.map((thing) => {
      console.log(this.foo, thing);
    });
  }
}

a.showFooForEach();

You could use bind() it to your context.

a = {
  foo: 'bar',
  things: [1, 2, 3],
  showFooForEach: function() {
    this.things.map(function(thing) {
      console.log(this.foo, thing);
    }.bind(this));
  }
}

a.showFooForEach();

That's because of JS lexical scope

From MDN:

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

Read more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

And here: http://javascriptissexy.com/javascript-apply-call-and-bind-methods-are-essential-for-javascript-professionals/

Also, map() does accept a second parameter as "this"

a = {
  foo: 'bar',
  things: [1, 2, 3],
  showFooForEach: function() {
    this.things.map(function(thing) {
      console.log(this.foo, thing);
    }, this);
  }
}

a.showFooForEach();

From MDN map() documentation:

Parameters

callback Function that produces an element of the new Array

thisArg Optional. Value to use as this when executing callback.

Further reading: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Short advice

PS.: Array.map is usually called when you want to do something with your array, for example adding 10 to each item, or something like that... Since the Array.map returns a new array. If you're only using console.log or something that wont affect the array itself you could just use a Array.forEach call instead

like image 38
Felipe Skinner Avatar answered Sep 24 '22 04:09

Felipe Skinner


There are three ways:

An plain-old variable

One that doesn't have the special oddness of this:

var self = this;
this.things.map(function(thing) {
  console.log(self.foo, thing);
});

Function.prototype.bind

this.things.map(function(thing) {
  console.log(this.foo, thing);
}.bind(this));

Use the second parameter of Array.prototype.map

The (optional) second parameter is the context that the inner function is called with.

this.things.map(function(thing) {
  console.log(this.foo, thing);
}, this);

The first two ways are generic ways of dealing with this; the third is specific to map, filter, forEach.

like image 20
Paul Draper Avatar answered Sep 24 '22 04:09

Paul Draper


Nothing complex needed! map takes a second param of thisArg, so you just pass it in with the function you want to invoke on each item:

a = {
  foo: 'bar',
  things: [1, 2, 3],
  showFooForEach: function() {
    this.things.map(function(thing) {
      console.log(this.foo, thing);
    }, this);
  }
}

a.showFooForEach();

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

like image 34
pherris Avatar answered Sep 24 '22 04:09

pherris


map allows a second argument called "thisArg" so you just use that like so:

showFooForEach: function() {
    this.things.map(function(thing) {
      console.log(this.foo, thing);
    },this);
like image 37
Satyajit Avatar answered Sep 25 '22 04:09

Satyajit