3

If I run this code, window object gets printed to console.

var arr= [1,2,34,5,6,7,7,8];
arr.forEach(function(e){
console.log(this);
});

Why does it not refer to arr object or specific items in array object? I want to understand the reason behind it, like what's going on. this gets defined using by new, or the object invoking this function, right?

Registered User
  • 468
  • 1
  • 7
  • 31
Muhammad Umer
  • 17,263
  • 19
  • 97
  • 168
  • 3
    It happens because that's how `forEach` invokes it. – zerkms Feb 24 '15 at 23:17
  • 3
    "why does this happen?" "because it happens" is not a good comment ;) – Mike 'Pomax' Kamermans Feb 24 '15 at 23:26
  • @Mike'Pomax'Kamermans I don't see how your comment is relevant. If one wanted to know why this was chosen as a default behaviour - they should have asked a different questsion. – zerkms Feb 24 '15 at 23:38
  • i specifically said **i want to understand the reason behind it** as in code structure and that leads to it – Muhammad Umer Feb 24 '15 at 23:49
  • @zerkms and that question would be closed, because we weren't there when it was decided. I'm with you; the implementation could have defaulted to having `this` be the current element (like jQuery's `each`) or the array itself, but it doesn't, and it pretty much _doesn't matter why_. – Evan Davis Feb 25 '15 at 01:07
  • 1
    @MuhammadUmer evey question about JS like "why it behaves like it behaves" have the same and the only answer: because the specification tells it to do so. – zerkms Feb 25 '15 at 01:08
  • @Mathletics that sounds a completely valid question why would you close it? It'd be insightful for new programmer to know of reasoning behind some decision made about the design of language. – Muhammad Umer Feb 25 '15 at 01:27
  • @MuhammadUmer as I said, __we were not there when the spec was written__, nor when it was implemented in the browser. Speculation on the nature of language idiosyncrasies is not appropriate for Stack Overflow. – Evan Davis Feb 25 '15 at 06:04
  • 2
    dont speculate if you don't know, but if you know say so. For one to have information one doesn't have to be a participant in creation of it or be present there. Inability to come up with a solid answer doesn't make the question bad. – Muhammad Umer Feb 25 '15 at 15:53
  • and as i said my question is asking two things a) why did people do it like this ( their reasoning) b) how is this the result of its implementation, programmatically? what code makes it point to window object. – Muhammad Umer Feb 25 '15 at 15:55

8 Answers8

6

.forEach() specifies the value of this within the iterator based on its 2nd parameter, thisArg.

arr.forEach(callback[, thisArg])

So, it will only use a particular object if you provide it:

arr.forEach(function(e){
    console.log(this);
}, arr); // <---

Otherwise, the value of this will be the default value of a normal function call -- either undefined in strict mode or the global object (window in browsers) in non-strict.

function foo(e) {
    console.log(this);
}

foo();            // [object Window]

[1].forEach(foo); // (same)

Though, the arr is still provided to the iterator, just as its 3rd argument:

arr.forEach(function (e, i, arr) {
    console.log(arr);
});
Jonathan Lonowski
  • 121,453
  • 34
  • 200
  • 199
4

This comes from two different factors of how the engine will determine the this value for the function, the thisArg optional parameter to forEach, and whether or not the code is in strict mode.

From MDN:

If a thisArg parameter is provided to forEach(), it will be passed to callback when invoked, for use as its this value. Otherwise, the value undefined will be passed for use as its this value. The this value ultimately observable by callback is determined according to the usual rules for determining the this seen by a function.

These rules are elsewhere documented as follows:

Inside a function, the value of this depends on how the function is called.

function f1(){
  return this;
}

f1() === window; // global object

In this case, the value of this is not set by the call. Since the code is not in strict mode, the value of this must always be an object so it defaults to the global object. (emphasis added)

Note that this behavior changes in strict mode. If you add "use strict" to the top of the call back, it will log undefined to the console.

In short, if you want the this value to refer to the array, arr, you just need to call forEach like this:

var arr= [1,2,34,5,6,7,7,8];
arr.forEach(function(e){
    console.log(this);
}, arr);
Community
  • 1
  • 1
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
3

Because the specification says so. Relevant parts:

15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )

...

5. If thisArg was supplied, let T be thisArg; else let T be undefined.

...

7.c.ii Call the [[Call]] internal method of callbackfn with T as the this value and argument list containing kValue, k, and O.

Now, we all know (hopefully) that if a function is to be called with a this value of null or undefined, this is set to the global object instead. Here is a refresher:

10.4.3 Entering Function Code

  1. If the function code is strict code, set the ThisBinding to thisArg.
  2. Else if thisArg is null or undefined, set the ThisBinding to the global object.
  3. ...

As you can also see, this will not point to the global object if we are not in strict mode (the default).


I want to understand the reason behind it

Why it was decided to do it this way can likely only be explained by someone who worked on the language. You could ask at https://esdiscuss.org or http://es-discourse.com/.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
2

this gets defined using by new, or the object invoking this function, right?

Yes. And in this case, it's forEach that invokes your function. Since no special precautions[1] are made, it simply passes nothing for this[2] just like any non-method function call, which will result into the global object as your function is sloppy code[3].

1: Like passing a second argument to forEach, which would be used for this
2: The spec says it should pass undefined
3: Meaning, that it does not use strict mode. In strict code, this would literally really be undefined.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

The function you've passed into forEach does not have the same context as your object. The forEach (as well as map etc) functions run detached from any object, so functions passed into them are executed in global scope by default.

You could be explicit and fix that in one of two ways.

  1. give the forEach a context to run in (as suggested by every other answer)

This uses code like:

var arr = [...];
var iterator = function(e) { console.log(this); };
arr.forEach(iterator, this);
  1. make your iteration function explicitly bound to your object first

this uses code like:

var arr = [...];
var iterator = function(e) { console.log(this); };
var iteratorWithThisContext = iterator.bind(this);
arr.forEach(iteratorWithThisContext);

The bind(<context>) function is a core javascript function for taking any function, and any context, and returning a new function that will always execute with the specified context when called, no matter who's doing the calling, or when:

var fn = function() { console.log(this); }
fn(); // window

var fndoc = fn.bind(document);
fndoc(); // document

var arr = [];
var fnarr = fn.bind(arr);
fnarr(); // the array
Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
0

From MDN:

If a thisArg parameter is provided to forEach(), it will be passed to callback when invoked, for use as its this value. Otherwise, the value undefined will be passed for use as its this value. The this value ultimately observable by callback is determined according to the usual rules for determining the this seen by a function.

If you expect this to be the array, you have to call arr.forEach(function(e){}, arr); using the optional second paramater, thisArg.

Evan Davis
  • 35,493
  • 6
  • 50
  • 57
0

Well that's very interesting.

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

You can include a second argument for the this object when calling forEach. If not provided, this will be limited to the same scope as if you were simply to write for (var i in arr) {.. }. I suspect they did this so that the forEach function behaves as closely to a built-in syntactical feature of JS as possible.

DRAB
  • 445
  • 2
  • 10
0

Because the function being called here is not being called as an object method, this refers to the current object context, and in a browser console window, that's the window object.

This Mozilla Developer Network article goes into great depth about what this means in different contexts.