0

I was using Node 13.12.0. Consider the following code snippet:

const A = function () {
    this.someArray = ['a', 'b', 'c'];

    this.forEach = this.someArray.forEach;
}

const a = new A();
a.forEach(item => console.log(item)); // should print the content of the array, but got nothing

When running the code above, no output was given nor any error was thrown. When putting a breakpoint inside the forEach callback, the breakpoint was not triggered.

But the code below does yield expected output:

const A = function () {
    this.doSomething = () => {
        console.log('hello');
    };
};

const B = function () {
    this.a = new A();

    this.doSomething = this.a.doSomething;
};

const b = new B();
b.doSomething(); // prints 'hello'

I have checked other questions about forEach but it seems that none of them relates to this question. Is there any explaination why they behave differently?

I have suspected that it might because this was different when called from different objects. But a quick check in a debugger turned out it was the same.

I also figured out that forEach is a native function. Does this have something to do with the question?

Daniel Lee
  • 161
  • 1
  • 9
  • You're losing the context when you just assign `forEach` as a function reference. If you want to do that, you need `this.forEach = this.someArray.forEach.bind(this.someArray)` – VLAZ Apr 14 '20 at 07:06
  • I guess not a perfect match but it does explain what's wrong in situations similar to the one in the question as well as how to handle those. – VLAZ Apr 14 '20 at 07:09

2 Answers2

2

You need to bind to the array someArray so that when you invoke the forEach it refers to someArray while iterating, since you have taken out the forEach from the someArray:

const A = function () {
    this.someArray = ['a', 'b', 'c'];
    //Binds the this context to this.someArray
    this.forEach = this.someArray.forEach.bind(this.someArray);
    // This is also equivalent
    //this.forEach = Array.prototype.forEach.bind(this.someArray);
}

const a = new A();
a.forEach(item => console.log(item)); 

You can also use an arrow function to preserve the this context from the lexical scope. The this from the lexical scope can be passed as the second argument to the forEach:

const A = function() {
  this.someArray = ['a', 'b', 'c'];
  //Using Arrow functions
  this.forEach = callback => this.someArray.forEach(callback, this)
}

const a = new A();
a.forEach(item => console.log(item));
Fullstack Guy
  • 16,368
  • 3
  • 29
  • 44
0

You're detaching the forEach method from its array, which causes this to point to the a instance instead of this.someArray when s.forEach is invoked.

I'd expect it to work if you changed it to:

this.forEach = (...args) => this.someArray.forEach(...args);

or if you changed it to:

this.forEach = this.someArray.forEach.bind(this.someArray);
ray
  • 26,557
  • 5
  • 28
  • 27