The invocation context (this) of the forEach function call
Asked Answered
D

5

36

I was wondering what the 'this' value (or invocation context) is of the forEach callback function. This code doesn't seem to work:

var jow = [5, 10, 45, 67];

jow.forEach(function(v, i, a){

    this[i] = v + 1;

});

alert(jow);

Thx for explaining it to me.

Descender answered 31/10, 2013 at 13:34 Comment(1)
See also Why “this” refers to Window in forEach in javascript?Berardo
D
10

I finished construction of the forEach method and wanted to share this diagram with everyone, hope it helps someone else trying to understand its inner workings.

The forEach method

Descender answered 31/10, 2013 at 14:33 Comment(3)
lol the spacing in this diagram doesn't actually mean anythingChloro
I mean the layout in general, this diagram is completely unreadableChloro
forEach() context should be inside of this context. In sdev, a context almost always wraps its children. I do like the UML style approach.Yah
U
57

MDN states:

array.forEach(callback[, thisArg])

If a thisArg parameter is provided to forEach, it will be used as the this value for each callback invocation as if callback.call(thisArg, element, index, array) was called. If thisArg is undefined or null, the this value within the function depends on whether the function is in strict mode or not (passed value if in strict mode, global object if in non-strict mode).

So in short, if you only provide the callback and you're in non-strict mode (the case you presented), it will be the global object (window).

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

Uneducated answered 31/10, 2013 at 13:37 Comment(7)
That's strange. If i create a method on an object, than the 'this' value will be that object. Why is this different?Descender
this inside the body of forEach will indeed be your particular array, but there is no connection between that and the callback you provide. The callback gets invoked however the implementation of forEach wants, specifically with thisArg, window or element as this.Uneducated
Thx, i'm getting there... i used the jow array as the thisArg, but i get bad results: jsfiddle.net/39LSg . Why is that? The value of -this- seems to jump with each iteration between the window object and the jow arrayDescender
You're using the value in the array as the index of the element you want to change. First iteration changes jow[1] to 5 (replaces 2 with 5), second changes jow[5] to 5 (adds another 5 at the end), third changes jow[3] to 5 (replaces 4 with 5), every other iteration changes jow[5] to 5.Uneducated
MDN says, In strict mode, however, the value of this remains at whatever it was set to when entering the execution context. In the context of a class method, a nested classic function passed as an argument of a forEach will keep the original class instance as the this value (given the function is not forced otherwise), sounds like arrow functions behavior doesn't it !?Copyholder
@Copyholder I'm not sure i understand what you mean. If you do [1,2,3].forEach(fn, obj), fn will be called 3 times equivalent to this: fn.call(obj, 1); fn.call(obj,2); fn.call(obj, 3);. If obj is not provided, this inside fn will resolve to the global object (not strict) or undefined (strict mode). Please note that calling fn creates a new execution context that has nothing to do with the execution context in which [...].forEach(...) was called.Uneducated
@Uneducated you are right, so do I, but recently some colleague's code used a classic function as a single argument to the Array forEach method, logging this value inside of it at runtime shown it was pointing to the surrounding context class instance (instead of expected undefined). This led me to wonder what was happening. In the end I suspect some Typescript transpiling stuff or webpack loader magic happening under the hood turning the fn into fat arrow fn. The MDN quote in my previous comment «the value of "this" remains at whatever it was» is confusing.Copyholder
D
10

I finished construction of the forEach method and wanted to share this diagram with everyone, hope it helps someone else trying to understand its inner workings.

The forEach method

Descender answered 31/10, 2013 at 14:33 Comment(3)
lol the spacing in this diagram doesn't actually mean anythingChloro
I mean the layout in general, this diagram is completely unreadableChloro
forEach() context should be inside of this context. In sdev, a context almost always wraps its children. I do like the UML style approach.Yah
B
7

If you dont pass second parameter to forEach, this will point to the global object. To achieve what you were trying to do

var jow = [5, 10, 45, 67];

jow.forEach(function(v, i, a) {
    a[i] = v + 1;
});

console.log(jow);

Output

[ 6, 11, 46, 68 ]
Breastbeating answered 31/10, 2013 at 13:39 Comment(0)
P
6

Inside forEach, this refers to the global window object. This is the case even if you call it from a different object (i.e. one you've created)

window.foo = 'window';

var MyObj = function(){
  this.foo = 'object';
};

MyObj.prototype.itirate = function () {
  var _this = this;

  [''].forEach(function(val, index, arr){
    console.log('this: ' + this.foo); // logs 'window'
    console.log('_this: ' + _this.foo); // logs 'object'
  });
};

var newObj = new MyObj();

newObj.itirate();
// this: window
// _this: object
Pood answered 15/7, 2016 at 16:34 Comment(2)
This is mostly true. But you can pass a thisArg after the callback to define what this refers to. In your example, you could call [''].forEach(function (…) {…}, myObj), so that this.foo would then return 'object' instead of 'window'.Oeo
And it's interesting to note that by default, the thisArg refers to the global window object (I've tested it in my browser), while in the specification it says that if no thisArg is provided, it should be undefined by default. MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Spec: ecma-international.org/ecma-262/5.1/#sec-15.4.4.18Oeo
S
2

I have a very simple approach for the 'this' context question and it goes like this: whenever you want to know what is the context of 'this', check who is left to the caller if there is no caller to the left than it is the global else it is that object instance:

Examples:

let obj = { name:"test", fun:printName }

function printName(){
  console.log(this.name)
}

//who is left to the caller? obj! so obj will be 'this'
obj.fun() //test

//who is left to the caller? global! so global will be 'this'
printName() //undefined (global has no name property)

So, for the 'foreach' case when you give a callback function what actually happens in foreach implementation is something like that:

--> you call [1,2,3].foreach(callback,'optional This')

 foreach(arr,cb)
 {
  for(i=0; i<arr.len;i++)
  {
   //who is left to the caller? global! so it will be 'this'
   cb(arr[i])
  }
 }

Unless - you give it the optional 'this' or you bind the callback with a this (for example arrow function) if that happens than the called callback already has a 'this' obj which kind of 'blocks' you from changing it's given context more on bind can be found here enter link description here But basically the bind implementation look as follows:

Function.prototype.bind = function (scope) {
    var fn = this;
    return function () {
        return fn.apply(scope);
    };
}

So you can see the fn (your callback) will always be called with your 'this' (scope)

Hope it helps...

Sachikosachs answered 20/2, 2019 at 8:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.