this
this
inside callback to forEach
class Counter { constructor() { this.sum = 0 } add(arr) { arr.forEach(function (item) { this.sum += item // <-- (this === undefined) }) } show() { console.log(this.sum) } } const counter = new Counter() counter.add([1, 2, 3]) counter.show()
add
function above will cause following TypeError as this
inside the anonymous callback function is undefined.
TypeError: Cannot read properties of undefined (reading 'sum')
This is because the callback function passed to forEach creates its own scope.
It makes this
inside the callback (this
=== undefined) and outside the callback (this
has access to sum: this.sum) different.
How to fix (bind) this
to callback function inside forEach:
Fix 1: optional argument to forEach
add(arr) { arr.forEach(function (item) { this.sum += item }, this) // <-- }
Syntax
forEach(callbackFn, thisArg)
thisArg (Optional): A value to use as
this
when executing callbackFn.
Fix 2: closure
add(arr) { const that = this // <-- arr.forEach(function (item) { that.sum += item // <-- that, not this }) }
Lexical context has changed inside the callback function.
The callback function still has access to variables in their enclosing scope (add(){...}
) throuth closure.
add
has access tothis
- callback doesn't have direct access to
this
- but callback has access to variables inside
add
- copy
this
insideadd
so callback can accessthis
Fix 3: bind
add(arr) { arr.forEach( function (item) { this.sum += item }.bind(this) // <-- ) }
Explicitly set the value of this
within the callback function.
Excerpt from mdn: this
The bind() method can set the value of a function's this regardless of how it's called.
Fix 4: arrow function
add(arr) { arr.forEach((item) => { this.sum += item; }); }
Excerpt from mdn: this
Arrow functions don't provide their own this binding (it retains the this value of the enclosing lexical context).