admin管理员组

文章数量:1333451

Just wanted to share a little trick I learned to pass variables into the scope of your JS Array.forEach() method.

I had a situation where I needed to use a forEach loop to build a dataset. But I needed to access variables in the current scope as well (I needed to be able to reference this in the loop).

This is the situation I was in:

var dataset = {
    data: [],
    backgroundColor:[],
};

items.forEach(function (item) {

    dataset.data.push(item.age);

    if (item.age < 2) {
        dataset.bgColor.push(this.green);
    } else if (item.age < 5) {
        dataset.bgColor.push(this.yellow);
    } else {
        dataset.bgColor.push(this.red);
    }

}, this);


this.refreshGraph(dataset);

Dataset isn't accessible from within the loop. So how do we access it while iterating?

I haven't seen this solution on stack overflow and it didn't fit any question I could find.

Answer below:

Just wanted to share a little trick I learned to pass variables into the scope of your JS Array.forEach() method.

I had a situation where I needed to use a forEach loop to build a dataset. But I needed to access variables in the current scope as well (I needed to be able to reference this in the loop).

This is the situation I was in:

var dataset = {
    data: [],
    backgroundColor:[],
};

items.forEach(function (item) {

    dataset.data.push(item.age);

    if (item.age < 2) {
        dataset.bgColor.push(this.green);
    } else if (item.age < 5) {
        dataset.bgColor.push(this.yellow);
    } else {
        dataset.bgColor.push(this.red);
    }

}, this);


this.refreshGraph(dataset);

Dataset isn't accessible from within the loop. So how do we access it while iterating?

I haven't seen this solution on stack overflow and it didn't fit any question I could find.

Answer below:

Share Improve this question asked Nov 19, 2019 at 18:02 Noah GaryNoah Gary 96012 silver badges26 bronze badges 1
  • 4 "Dataset isn't accessible from within the loop.", Not sure what you mean, dataset should be visible within the forEach callback as it is in a higher scope, higher scoped variables are visible to lower scoped code – Patrick Evans Commented Nov 19, 2019 at 18:11
Add a ment  | 

5 Answers 5

Reset to default 2

If you have a function out of scope of some data yet need to access it, you can use a curried function that takes that dataset as the first parameter and can still use this normally throughout:

//curried function that uses `dataset` and `this` but it is not 
//in the context where the iteration happens 
function makeLoopCallback(dataset) {
  return function(item) {
    dataset.data.push(item.age);

    if (item.age < 2) {
        dataset.bgColor.push(this.green);
    } else if (item.age < 5) {
        dataset.bgColor.push(this.yellow);
    } else {
        dataset.bgColor.push(this.red);
    }
  }
}

//object to serve as `this` context for a function
var obj = {
  green: "Green",
  yellow: "Yellow",
  red: "Red",
  doSomething: function(items) {
    var data = {
        data: [],
        bgColor:[],
    };
  
    items.forEach(makeLoopCallback(data), this);
  
    return data;
  }
}

//set up some dummy data
var input = [ { age: 1 }, { age: 2 }, { age: 3 }, { age: 4 }, { age: 5 }, { age: 6 } ];

//call the function
console.log(obj.doSomething(input))

An alternative is to use Array#reduce instead of Array#forEach with a function that takes two parameters directly. Since .reduce cannot set the this context, you can just use Function#bind to do it:

//external function that uses `dataset` and `this` but it is not 
//in the context where the iteration happens
function external(dataset, item) {
    dataset.data.push(item.age);

    if (item.age < 2) {
      dataset.bgColor.push(this.green);
    } else if (item.age < 5) {
      dataset.bgColor.push(this.yellow);
    } else {
      dataset.bgColor.push(this.red);
    }

    return dataset;
}

//object to serve as `this` context for a function
var obj = {
  green: "Green",
  yellow: "Yellow",
  red: "Red",
  doSomething: function(items) {
    var data = {
        data: [],
        bgColor:[],
    };

    return items.reduce(external.bind(this), data);
  }
}

//set up some dummy data
var input = [ { age: 1 }, { age: 2 }, { age: 3 }, { age: 4 }, { age: 5 }, { age: 6 } ];

//call the function
console.log(obj.doSomething(input))

With the abilities of es6 If you'll use an Arrow Function the this will be taken from

items.forEach(item => {
// You can use this as out of the forEach scope
});

From MDN Web Docs:

An arrow function does not have its own this. The this value of the enclosing lexical scope is used; arrow functions follow the normal variable lookup rules. So while searching for this which is not present in current scope, an arrow function ends up finding the this from its enclosing scope.

Another nice explanation: https://hackernoon./javascript-es6-arrow-functions-and-lexical-this-f2a3e2a5e8c4

I found a simple one

if you don't need a context, you can simply use it to pass anything you want.

response.services.forEach(function(service) {

   //code

},undefined, <whatever i want to pass>);

just passed an undefined in second parameter and in third parameter i pass extra arguments into JS Array.forEach()

Short Example

[1,2,3].forEach( myCallback, undefined, 'additionalFoo' );

#ref

The solution is to pass a JSON object as the this argument.

so before we had:

Array.forEach(function(){}, this) 
// "this" is just an object ^^^^ just like anything else in JavaScript

Now we have:

Array.forEach(function(){}, {_self: this, dataset: dataset}) 

// you can access _self and dataset just as if they were in scope

And now you can make data changes while iterating with an anonymous function :)

Full example:

var dataset = {
    data: [],
    backgroundColor:[],
};

items.forEach(function (item) {

    dataset.data.push(item.age);

    if (item.age < 2) {
        dataset.bgColor.push(_self.green);
    } else if (item.age < 5) {
        dataset.bgColor.push(_self.yellow);
    } else {
        dataset.bgColor.push(_self.red);
    }
}, { _self: this , dataset: dataset});

Array.prototype.forEach(callbackFun, ?this)

You can pass dataset as this argument to forEach

var dataset = {
data: [],
backgroundColor:[],
};

items.forEach(function (item) {

this.dataset.data.push(item.age);

if (item.age < 2) {
    this.dataset.bgColor.push(this.tempThis.green);
} else if (item.age < 5) {
    this.dataset.bgColor.push(this.tempThis.yellow);
} else {
    this.dataset.bgColor.push(this.tempThis.red);
}

}, {tempThis:this,dataset:dataset});


this.refreshGraph(dataset);

本文标签: javascriptHow to pass extra arguments into JS ArrayforEach()Stack Overflow