33

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?

Adam Grant
  • 12,477
  • 10
  • 58
  • 65
  • The accepted answer to this question should address your question: https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback – mittmemo Aug 06 '15 at 21:52
  • 1
    Not really sure how this would go! When making a normal OOP project I might make say something like `var myself = this;` and then access `myself` from within the anonymous function. – Nebula Aug 06 '15 at 21:53

5 Answers5

37

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));
Adam Grant
  • 12,477
  • 10
  • 58
  • 65
13

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

Felipe Skinner
  • 16,246
  • 2
  • 25
  • 30
9

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.

Paul Draper
  • 78,542
  • 46
  • 206
  • 285
7

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

pherris
  • 17,195
  • 8
  • 42
  • 58
3

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);
Satyajit
  • 3,839
  • 1
  • 19
  • 14