0

I'm new to JS and I recently studied about objects and functions. Here I want to call a function for each object in an array of objects. I'm using forEach to do so. The result is undefined for each property.

function showDetail() {
  console.log(`name: ${this.name}
         price: ${this.price}
         sold: ${this.sold}
         console: ${this.console}
         `);
}
const games = [{
    name: ' Crash Bandicoot N. Sane Trilogy',
    price: 1060,
    sold: 20,
    console: 'PS4',
  },
  {
    name: 'Lego Marvel Super Heroes',
    price: 700,
    sold: 25,
    console: 'XBOX',
    showDetail: function() {
      console.log(this.name);
    }
  },
  {
    name: 'Gta V',
    price: 1449,
    sold: 30,
    console: 'PS4',
    showDetail: function() {
      console.log(this.name);
    }
  }
];
games.forEach(showDetail);

The result is like this for each object:

name: undefined
     price: undefined
     sold: undefined
     console: [object Object]
 games.forEach(showDetail);
2
  • To bind the game as context, you can do games.forEach(i => { showDetail.bind(i)() }); Commented May 15, 2018 at 16:55
  • @ChrisG: There's no reason to use .bind() like that. If you're calling the function immediately, you use .call or .apply to set the value of the thisArg. games.forEach(obj => showDetail.call(obj)); Commented May 15, 2018 at 17:01

3 Answers 3

3

You should pass a 'game' as parameter to the function and print its properties, not from 'this'

function showDetail(game){
   console.log(`name: ${game.name}
   price: ${game.price}
   sold: ${game.sold}`);
}

const games = [ ..... ]
games.forEach( function(game) { showDetail(game) });
//if there is support for arrow functions, it's cleaner
games.forEach( game => showDetail(game) );
//also, as mentioned by Máté in the comments
games.forEach( showDetail ); //will work

If you want the 'showDetail' function to work with this 'this', you should bind the 'game' to the function

games.forEach( game => showDetail.bind(game)() );
function showDetail(){
   //here the 'this' is the 'game', because it has been bounded via .bind() method
}

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

Sign up to request clarification or add additional context in comments.

12 Comments

games.forEach( function(game) { showDetail(game) }); is overkill. games.forEach(showDetail); works just fine, since showDetail() will receive the game as a parameter. In this case at least, when the argument can be passed as-is.
Thanks @MátéSafranka I'm not used to this approach, I usually expose the parameters. I didn't remeber this way you mentioned will work. I have updated my answer.
Yeah I edited my comment a bit to clarify as well. It's more common that we need to transform the arguments in some way before passing them to the callback, so it doesn't hurt if OP sees that option too.
@MátéSafranka Thank you. Why is this not working? Foreach is iterating through the array of objects and the function should know that it's an object.
Because the function isn't being invoked as a method of the object. You're probably confusing it with event handlers.
|
2

When you write

games.forEach(showDetail);

showDetails is a callback function of forEach and it is passed the object as the first parameter and hence you would write

function showDetail(game) {
  console.log(`name: ${game.name}
         price: ${game.price}
         sold: ${game.sold}
         console: ${game.console}
         `);
}

when you this inside showDetail, the this value is not binded to the object context and hence this.name doesn't return the object value. However if you write

games.forEach(game => {
   showDetail.call(game);
});

you are providing the context to be object and this case this.name inside showDetail will work

3 Comments

@ChrisG, well I did add more details on why OP code was not giving the desired output
@ChrisG: This answer gives more information. Which answer is "first" is irrelevant.
@CrazyTrain I'm aware of that, thanks. I posted before he edited his answer.
0

Maybe you mean this

// constructor

function Game(parms) {
  this.name = parms.name;
  this.price = parms.price;
  this.sold = parms.sold;
  this.gconsole = parms.gconsole;
}

// toString override

Game.prototype.toString = function() {
  return `name: ${this.name}
         price: ${this.price}
         sold: ${this.sold}
         gconsole: ${this.gconsole}
         `;
}

const games = [
  new Game({
    name: ' Crash Bandicoot N. Sane Trilogy',
    price: 1060,
    sold: 20,
    gconsole: 'PS4'
  }),
  new Game({
    name: 'Lego Marvel Super Heroes',
    price: 700,
    sold: 25,
    gconsole: 'XBOX'
  }),
  new Game({
    name: 'Gta V',
    price: 1449,
    sold: 30,
    gconsole: 'PS4'
  })
];
games.forEach(function(g) {
  console.log(g.toString());
});

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.