21

var f = function() {
  this.x = 5;
  (function() {
    this.x = 3;
  })();
  console.log(this.x);
};

f.call(f);

f();

f.call();

Running var f as f.call(f) outputs 5. When running it as f() or f.call() outputs 3.

What happens in each case? What does the inner function's this refer to?

Derek Pollard
  • 6,953
  • 6
  • 39
  • 59
El Anonimo
  • 1,759
  • 3
  • 24
  • 38
  • 3
    Don't confuse the two this references. Instead try to see there are two different stack frames. Each frame has a this reference. That this is either the default or it's been set by call(). The inner function can not be set. It's always the default. I think the default for this is window, but that depends if there is a module loader or not. I should know but I can't remember. – Reactgular May 06 '19 at 15:24
  • 3
    If you want to add some other scopes/context, use the `.bind(context)` method. – Adrian Preuss May 06 '19 at 15:28
  • This is why you always should use strict mode… – Bergi May 06 '19 at 16:45
  • 1
    It's also why you should use lambdas instead of anonymous functions – BlueRaja - Danny Pflughoeft May 06 '19 at 18:21

5 Answers5

13

First Case:

In the first you are calling the function. And inside the function the function itself i.e f is set as this. So in first example this.x = 5; sets the property x on the function.

When the inner function is called this refers to window object so this.x = 3; changes the x property of window object.

When it logs console.log(this.x); here the same property x which was set as property of function is logged.

Second Case:

In the second example this inside the outer function refers to window so when this.x = 3; is evaluated the window.x becomes 3. As this refers to window in outer function so console.log(this.x); logs window.x which is 3

Conclusion:

The conclusion of the whole discussion is that if no argument is passed to call() then automatically window object is binded. According to MDN

thisArg

Optional. The value of this provided for the call to a function. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode, null and undefined will be replaced with the global object and primitive values will be converted to objects.

See the below snippet.

function foo(){
  console.log(this);
}
foo.call(foo); //foo function
foo.call(); //window object
Community
  • 1
  • 1
Maheer Ali
  • 35,834
  • 5
  • 42
  • 73
5

If there is no specific context, this will be window. Your inner function always runs without a context, so it'll set window.x to 3. If you call f(), it will also run with this being window therefore logging the 3.

If you however do f.call(f), this will be the f function object, and it's x property will be set to 5.

  f.call(f)
  console.log(
    f.x, // 5
    window.x // 3
  );

I'd recommend stepping through it with the debugger if it isn't clear yet.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
2

When you call f with a reference to itself, it sets the x property to 5 on the function and the inner anonymous function has its this referring to the window, so it sets window.x to 3. Outside the anonymous function, this still refers to the function f, so console.log(this.x) outputs 5.

When you invoke f using f() or f.call() the function f and the anonymous function inside it have a this reference set to the window (the default) and so changing the value of this.x inside or outside the anonymous function affects the output result.

You can see this clearly if you console.log the values of this inside the function f and inside the inner anonymous function.

var f = function() {
 console.log("This inside function f:", this.toString());
  this.x = 5;
  (function() {
   console.log("This inside anonymous inner function:", this.toString());
    this.x = 3;
  })();
  console.log(this.x);
};
console.log("calling function x with this set to itself");
f.call(f);
console.log("---------------")
console.log("invoking function x with brackets ()")
f();
console.log("---------------")
console.log("calling function x without setting this context")
f.call();
console.log("---------------")
Unmitigated
  • 76,500
  • 11
  • 62
  • 80
1

Further to the other answers, should you want predictable behaviour, you have at least 2 methods available to you.

Method 1: (closure)

var f = function() {
  this.x = 5;
  var that = this;
  (function() {
    that.x = 3;
  })();
  console.log(this.x);
};

f.call(f); // 3

f(); // 3

f.call(); // 3

Method 2: (arrow function)

var f = () => {
  this.x = 5;
  (function() {
    this.x = 3;
  })();
  console.log(this.x);
};

f.call(f); // 3

f(); // 3

f.call(); // 3
Adrian Bartholomew
  • 2,506
  • 6
  • 29
  • 37
0

'this' keyword refers to the context object in which the current code is executing. Outside any function this refers to the global object.

By using call(), apply() functions the value of this can pass from one context to another context.

  • So in the 1st case [f.call(f)] by calling the call function and giving the first argument as f, this.x refers to 5. So answer is printed as 5.
  • 2nd case [f()] it is calling the function without using call function. So it refers the execution context of this as 3. So it prints as 3.
  • In the third case [f.call()], inside the call function no argument is mentioned. So it refers to the global object and prints 3 as the output.
Swifty
  • 839
  • 2
  • 15
  • 40