Understanding “this” binding in Javascript

Yasemin çidem
Nerd For Tech
Published in
5 min readMar 17, 2021

In order to understand “this” keyword clearly, we need to go through of how the execution context works at first. Every time you run some Javascript code, the engine creates a Global Execution Context. Every time a function is invoked, a brand new Local Execution Context is created for that function. Each function has its own execution context, but it’s created when the function is invoked or called. There can be only one Global Execution Context, there can be any number of Local Execution Contexts in one program.

What execution context looks like ?

The execution context is created during the creation phase. Following things happen during the creation phase:

  1. LexicalEnvironment component is created.
  2. VariableEnvironment component is created.

What Lexical Environment looks like ?

Let’s look at following example to understand the Lexical Environment:

const person = {
name: 'yasemin',
birthYear: 1991,
calcAge: function() {
console.log(2018 - this.birthYear);
}
}
person.calcAge();
const calculateAge = person.calcAge;
calculateAge();

The following code snippet shows of how Lexical Environment looks like conceptually.

GlobalExecutionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
}
outer: < null >,
this: < global object >
}
}
FunctionExecutionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
}
outer: < Global or outer function environment reference>,
this: depends on how function is called
}
}

As you see above example, each Lexical Environment has three components:

  1. Environment Record
  2. Reference to the outer environment,
  3. This binding

In this article we are going to tackle “this” binding.

What is “this” binding ?

As you see above example, “this” binding is a property of an execution context (global, function or eval). It behaves depends on the execution context.

Global Execution Context:

  • In the global execution context, the value of “this” refers to the global object. (in browsers, this refers to the Window Object).

Function execution context:

  • In the function execution context, the value of “this” depends on how the function is called. Each function invocation defines its own context, therefore, the “this” behaves differently than you may expect.

In javascript there are many ways to invoke the functions. Let’s see what they are.

Default binding:

Default binding is what happens if you don’t use any other kind of binding. If we call any function only parentheses, we will get default binding. It acts differently behave “strict” mode.

In the non-strict mode, the this references the global object when the function is called as follows:

function show() {   
console.log(this === window); // true
}
show();

In strict mode, Javascript sets this to undefined . Note that the strict mode has been available since ECMAScript 5.1. The strict mode applies to both function and inner functions within the function.

function show() {  
"use strict";
console.log(this === undefined); // true
function display() {
console.log(this === undefined); // true
}
bar();
}
show();

Implicit binding:

Implicit binding is what happens if you call a function with a “.” before it. In other words, it is calling a method of an object.

const obj = {
foo: function() {
console.log(this); // {foo: f} which is basically obj.
},
};

obj.foo();

Also you can store it in a variable, call the method via the variable.

const obj = {
name: 'Obj',
foo: function() {
console.log(this.name); // undefined
},
};

const newObj = obj.foo();
newObj.foo()

It logs undefined because when you call a method without specifying its object, JavaScript sets this to the global object in non-strict mode and undefined in the strict mode.

Note: To fix this issue, you can use bind method. We will take a look at it later.

New binding:

New binding is what happens when you use the new keyword to create an instance of a function object, you use the function as a constructor. You can use new when calling functions like so: new foo()

new does 4 things:

  1. It creates a new empty object.
  2. It makes this be the new object.
  3. It makes foo.prototype be the prototype of the object.
  4. It implicitly returns this if nothing else is returned from the function.
function foo() {
console.log(this); // outputs an empty object
}

new foo();

Explicit binding:

In JavaScript, functions are first class citizens. In other words, functions are objects, which are instances of the function type which has two methods: call() and apply() . Explicit binding is what happens if you call a function by using one of the three functions call, apply or bind, present in function objects.

  • call: it accepts additional arguments that are comma separated. They will be passed to the function call. foo.call(obj, argument1, argument2)
  • apply:it is very similar to the call, the only difference is that it accepts arguments in an array. foo.apply(obj, [argument1, argument2])
function Product(name, price) {
this.name = name;
this.price = price;
}

function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}

function Toy(name, price) {
Product.call(this, name, price);
this.category = 'toy';
}

const cheese = new Food('feta', 5);
const fun = new Toy('robot', 40);
console.log(`${cheese.name} is ${cheese.category}`);//feta is a food
console.log(`${fun.name} is ${fun.category}`); //robot is a fun
  • bind:It returns a new function, when invoked, has its this sets to a specific value. foo.bind(thisArg[,arg1[,arg2[,..]]]) Unlike the call() and apply() methods, the bind() method doesn’t immediately execute the function. It just returns a new version of the function whose this sets to thisArg argument. When you pass a method an object is to another function as a callback, the this is lost. For example:
let person = { 
name: ‘John Doe’,
getName: function() {
console.log(this.name); // undefined
}
};
setTimeout(person.getName, 1000);

It can be rewritten as the following:

let f = person.getName; 
setTimeout(f, 1000); // lost person context

Using method as a callback means that calling them with default binding. (We have already discussed it in default binding scope).Therefore, when the callback person.getName is invoked, the name does not exist in the global object, it is set to undefined. There are to ways to fix this issue:

1. Wrap it in another function
2. Use bind method

let f = person.getName.bind(person); 
setTimeout(f, 1000);

Beside this benefit of bind method. Also we can make use of the bind method to borrow methods from a different object without making a copy of that method as call and apply did.

const person1 = {
name: "John",
age: 15,
displayAge: function(){
console.log("He is " + this.age + " years old");
}
};
person1.displayAge(); /*Output: He is 15 years old*/
const person2 = {
name: "Mike",
age: 20
};
person1.displayAge.call(person2); //Output: He is 20 years oldconst displayAge = person1.displayAge.bind(person2)
displayAge(); //Output: He is 20 years old
person1.displayAge.apply(person2); //Output: He is 20 years old

Arrow functions:

The arrow function does not create its own execution context, but inherits the this from the outer function where the arrow function is defined. See the following example:

const obj = {
foo:() => console.log(this) //window object depends on strict mode
};

obj.foo();

This is an an example of usage of the arrow function with new binding.

function Person() {
const foo = () => console.log(this.name); // Yasemin.
this.name = "yasemin";
}

const person = new Person();
person.foo();

We call new Person(). This creates a new empty object and binds it as the value of this. When we call the method of foo, this points currently the object that new created.

--

--