1

I have a simple example below which creates an Object constructor. There is an add method which attempts to iterate through the arguments collection by calling the Array.prototype.forEach method.

The technique works, but I’m having a problem with the this value.

I have resorted to the trick of assigning this to a real variable (This), an using the the real value in the inner function. This works, but I though it should be simpler.

According to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call, I should be able to call this with an optional thisArg at the beginning. I have tried this in the add2 method. This doesn’t work.

What is the correct way of including a new value for this argument in the call method?

function Collection() {
  this.data=[];
}
Collection.prototype={
  constructor: Collection,
  add() {
    var This=this;
    Array.prototype.forEach.call(arguments,function(value) {
      This.data.push(value);
    });
  },
  add2() {
    Array.prototype.forEach.call(this,arguments,function(value) {
      this.data.push(value);
    });
  },
}

var c=new Collection();
c.add('apple','banana');
alert(c.data)
Manngo
  • 8,349
  • 6
  • 50
  • 74
  • Just use arrow functions instead, which inherit their `this` from the outer scope rather than gaining it from their calling context. Also consider using argument rest syntax rather than `arguments`. – CertainPerformance Oct 06 '18 at 01:43
  • Actually, I’m writing a polyfill which precludes arrow functions. I know they would make life easier. – Manngo Oct 06 '18 at 01:45
  • If you can't use arrow functions, then `that = this` and then referring to `that` later is pretty much the only other way to do it. But if at all possible, write in the latest syntax possible and then use Babel to transpile it later - makes for fewer headaches like these. – CertainPerformance Oct 06 '18 at 01:47

1 Answers1

1

forEach takes an optional parameter for this which it will use inside the callback. It should be a good option if you can't use arrow functions:

function Collection() {
    this.data=[];
  }
  Collection.prototype={
    constructor: Collection,
   
    add2() {
      Array.prototype.forEach.call(arguments,function(value) {
        this.data.push(value);
      }, this); // <= pass this in to forEach
    },
  }
  
  var c=new Collection();
  c.add2('apple','banana');
  console.log(c.data)
Mark
  • 74,559
  • 4
  • 81
  • 117
  • I can’t accept the answer yet (I have to wait another 5 minutes for some reason), but thanks. Would you mind checking the information on this link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call#Syntax to see whether I have misconstrued it? I’m sure that it suggests that the `this` argument should be first. – Manngo Oct 06 '18 at 01:52
  • @Manngo, yes that's a little confusing. In the example above, `this` is being passed into `forEach()` not `call()`. It's the last optional parameter listed here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach – Mark Oct 06 '18 at 01:54
  • 1
    I understand now. The argument list belongs to the `forEach` method, not the `call` method. Thanks again. – Manngo Oct 06 '18 at 01:57