DEV Community

loading...
Cover image for JavaScript 'this' - Four rules to know

JavaScript 'this' - Four rules to know

Kingsley Ubah
Web Developer. Technical Writer. Educator. Freelancer. African in Tech. I write tutorials on web/software development.
Originally published at ubahthebuilder.tech ・5 min read

When you use this in your code, it automatically resolves to an object or scope depending on the context at which is was defined.

But what are those possible contexts a this call can refer to? Additionally, how can we use that information to find out which object a this call will resolve to? Let's find out!

this Context

When used in a function, the this keyword simply points to an object which it is bound to. In simple terms, it answers the question of where it should get some value or data from:

function alert() { 
  console.log(this.name + ' is calling'); 
}
Enter fullscreen mode Exit fullscreen mode

In the function above, the this keyword is simply referring to an object to which it is bound to access the "name" property from it.

But how do you know what object or scope the function is bound to? How do you make out what this is referring to?

To unravel that, we need to understand the various binding rules that guide the this behavior.

Types of Binding in JavaScript

Generally, there are four kinds of bindings:

  • Default Binding
  • Implicit Binding
  • Explicit Binding
  • Constructor Call Binding

Default Binding in JavaScript

One of the first rules to remember is that if the function housing a this reference is a standalone function, then that function is bound to the global object.

function alert() { 
  console.log(this.name + ' is calling'); 
}


const name = 'Kingsley'; 
alert(); // Kingsley is calling
Enter fullscreen mode Exit fullscreen mode

name() is a standalone, unattached function. As per the rule, it is bound to the global scope. Hence, the this.name reference resolves to the global variable const name = 'Kingsley'.

This rule, however, doesn't hold if name() were to be defined in strict mode as so:

function alert() { 
  'use strict'; 
  console.log(this.name + ' is calling'); 
}

const name = 'Kingsley'; 
alert(); // TypeError: `this` is `undefined`
Enter fullscreen mode Exit fullscreen mode

Implicit Binding in JavaScript

Another scenario to look out for is whether the function is attached to an object (its context) at the call site.

According to the binding rule in JavaScript, a function can use an object as its context only if that object is bound to it at the call site. This form of binding is known as implicit binding.

Here is what I mean by that:

function alert() { 
  console.log(this.age + ' years old'); 
}

const myObj = {
  age: 22,
  alert: alert
}

myObj.alert() // 22 years old
Enter fullscreen mode Exit fullscreen mode

Put simply, when you call a function using dot notation, this is implicitly bound to the object the function is being called from.

In this example, since alert is being called from myObj, the this keyword is bound to myObj. So when alert is called with myObj.alert(), this.age is 22, which is the age property of myObj.

Let's look at another example:

function alert() { 
  console.log(this.age + ' years old'); 
}

const myObj = {
  age: 22,
  alert: alert,
  nestedObj: {
    age: 26,
    alert: alert
  }
}

myObj.nestedObj.alert(); // 26 years old
Enter fullscreen mode Exit fullscreen mode

Here, because alert is ultimately being called from nestedObj, this is implicitly bound to nestedObj instead of myObj.

An easy way to figure out which object this is implicitly bound to is to look at which object is to the left of the dot (.):

function alert() { 
  console.log(this.age + ' years old'); 
}

const myObj = {
  age: 22,
  alert: alert,
  nestedObj: {
    age: 26,
    alert: alert
  }
}

myObj.alert(); // `this` is bound to `myObj` -- 22 years old
myObj.nestedObj.alert(); // `this` is bound to `nestedObj` -- 26 years old
Enter fullscreen mode Exit fullscreen mode

Explicit binding in JavaScript

We saw that implicit binding had to do with having a reference in that object.

But what if we want to force a function to use an object as its context without putting a property function reference on the object?

We have two utility methods to achieve this: call() and apply().

Along with a couple other set of utility functions, these two utilities are available to all functions in JavaScript via the [[Prototype]] mechanism.

To explicitly bind a function call to a context, you simply have to invoke the call() on that function and pass in the context object as parameter:

function alert() { 
  console.log(this.age + ' years old'); 
}

const myObj = {
  age: 22
}

alert.call(myObj); // 22 years old
Enter fullscreen mode Exit fullscreen mode

Now here's the fun part. Even if you were to pass around that function multiple times to new variables (currying), every invocation will use the same context because it has been locked (explicitly bound) to that object. This is called hard binding.

function alert() { 
  console.log(this.age); 
} 

const myObj = { 
  age: 22 
}; 

const bar = function() { 
  alert.call(myObj); 
}; 

bar(); // 22
setTimeout(bar, 100); // 22 
// a hard-bound `bar` can no longer have its `this` context overridden 
bar.call(window); // still 22
Enter fullscreen mode Exit fullscreen mode

Hard binding is a perfect way to lock a context into a function call and truly make that function into a method.

Constructor Call Binding in JavaScript

The final and perhaps most interesting kind of binding is the new binding which also accentuates the unusual behaviour of JavaScript in comparison to other class-based languages.

When a function is invoked with the new keyword in front of it, otherwise known as a constructor call, the following things occur:

  • A brand new object is created (or constructed)
  • The newly constructed object is [[Prototype]]-linked to the function that constructed it
  • The newly constructed object is set as the this binding for that function call.

Let's see this in code to get a better understanding:

function giveAge(age) { 
  this.age = age; 
} 

const bar = new giveAge(22); 
console.log(bar.age); // 22
Enter fullscreen mode Exit fullscreen mode

By calling giveAge(...) with new in front of it, we’ve constructed a new object and set that new object as the this for the call of foo(...). So new is the final way that you can bind a function call’s this .

SUMMARY

To summarize, the this keyword, when used in a function, binds that function to a context object.

There are four kinds of bindings: default binding, implicit binding, explicit binding and constructor call binding (new).

Knowing these four rules will help you easily discern the context for a this reference.

I hope you got something valuable from this article. If you are learning JavaScript, here are some other articles that might interest you:

Thank you for reading and see you soon.

P/S: If you are learning JavaScript, I created an eBook which teaches 50 topics in JavaScript with hand-drawn digital notes. Check it out here

Discussion (0)