How [this] happened

August 04, 2021

post-bg

Content
Binding of this established at runtime, when the function is invoked
What determines this is how the function is invoked.
Where the function is defined is irrelevant in determining this value.
The value of this is always an object.

In JavaScript, the value of this is determined by the invocation context of the function. A common misconception is often associated with the function method's placement - inside of an object, and its host object, dictating what the this binding should be; when in fact it is the object-dot-method invocation, that sets its binding value.

In the example below, sharing the global scope we have two declared variables: size and myObj alongside a function method call for: myObj.sizeFun(), which determines this value at runtime.

//value of [this] when window scope.
var size = 'small';
//this crazy
this.size = 'medium';


var myObj = {  //parent object not the reason
  //why [this] value is large instead of small
  size: 'large',
  sizeFun: function() {//function
    console.log(this.size);  //value of [this] determined at runtime.
  }
}
//what determines the value of [this]
myObj.sizeFun();

Value of This by invocation

In the first three function calls below, this binding is implicit - set by the JavaScript engine. The last type uses call and apply during the function invocation to have some say over its binding, thus - explicitly set by the user.

1 - Normal function invocation 2 - Function as a method attached to an object 3 - Function constructor using new keyword 4 - Function call with either call() and apply()

Normal function invocation

Calling function after it is defined

Below, we have a declared function, and we want to know what the value of this is inside of it. Next, proceed to call fun1; which sets the value of this to the global scope, and the name variable to global.

var name = 'global';

var fun1 = function() {
    var name = "fun1";
    console.log(this);
    console.log(this.name);
}

fun1();

Calling a function from another

A function call from inside of another function. We first defined both functions, and proceed to invoke fun1. Fun1 is executed and fun2 then called from within fun1. Despite calling fun2 from within fun1, it still returns the same window scope for this and global type string value for name.

var name = 'global';

var fun1 = function() {
  var name = "fun1";
  console.log(this);
  console.log(this.name);
  fun2();
}

var fun2 = function() {
  var name = "fun2";
  console.log(this);
  console.log(this.name);
}

fun1();

//same as
this.fun1()

Returning a function call

Returning a function from a function call. Here, we declared a function and a variable. Fun1 is set to return an anonymous function this time, and we want to find out the value of this inside of it. To call the anonymous function, instead of just returning the function, we first assign fn1 invocation to a new variable x.

var name = 'global';

var fun1 = function() {
  var name = "fun1";
  console.log("From fun1---");
  console.log(this);
  console.log(this.name);
  return function() {
    var name = "fun2";
    console.log("From fun2---");
    console.log(this);
    console.log(this.name);
  }
}

var x = fun1();
x();

Defining the function in this manner did not change the binding of this during the function call, and much less the value of global.

Passing function as an argument in another function

First-class functions can be treated as values, and pass as arguments of another function. Here fun1 and fun2 are defined along with the name variable. Fun2 passed in as an argument in fun1.

var name = 'global';

var fun1 = function(fn) {
  var name = "fun1";
  console.log(this);
  console.log(this.name);
  fn();
}

function fn2() {
  var name = "fun2";
  console.log(this);
  console.log(this.name);
}

fun1(fn2);

Just as before, the value of this is the window scope and the value of name is global. When invoking a normal function, the value of this is the global scope.

Function invocation - Strict mode

In strict mode, it works differently. It doesn't work at all. A normal function invocation in strict mode below.

'use strict';
var name = 'global'
var fun0 = function() {
    var name = "local";
    console.log(this);
    console.log(this.name);
}
fun0;

In strict mode, the global object is no longer available for binding, and the value of this inside of fn0 is undefined and the name variable now, it'll return an error.

Method invocation of a function

The binding of this is determined by how the method is invoked and not where is defined.

Object - method invocation

Here we invoke obj1 with method fn1. As a result, the binding of this is the object.

var name = 'global';

var obj1 = {
  name: 'local',
  fun1: function() {
    console.log(this);
    console.log(this.name);
  }
}

obj1.fun1();

The binding of this has nothing to do with the method being defined inside of the object, and everything to do with the object being used to invoke the method.