Telerik blogs
JavaScriptT2 Light_1200x303

In this article, we will look at what the this keyword is and the four rules that determine its behavior in JavaScript.

Compared to other languages, the this keyword acts differently in JavaScript. It is one of the most common JavaScript keywords. As confusing as it can be, it is a fundamental concept because it allows for flexibility, reusing a function in multiple contexts.

What Is the ‘This’?

When you invoke a function in JavaScript, a new execution context is created and added to the call stack. The execution context contains a this reference or a this binding that will be used throughout the function’s execution. What this references is entirely determined by the call site (the location where a function is called, not where it is declared).

So, for every function invocation, there is a special identifier called this, but this isn’t concerned with where a function is defined; instead, it is interested in how it is called.

The this keyword can be used to invoke functions in different contexts, and depending on the context, this might mean something entirely different every time. The this keyword never points at the function itself. It always points to a context object that can be identified by looking at the call site of a function. How then can we determine what the value of this points at for any given function?

4 Rules for Binding ‘This’

In JavaScript, there are four different ways to invoke a function, and each one of them provides different context objects for the this keyword to point at when executing a function. The four rules for binding this are determined by how we invoke functions in JavaScript. Understanding these four ways can help us understand how the this keyword gets bound.

Implicit Binding

When we invoke a function in the context of an object that owns or contains the function’s this, it will point to that object.

Consider the following example:

function sayHello() {
  console.log(this.greet)
}
var greet1 = {greet: 'Hello', sayHello: sayHello}
var greet2 = {greet: 'hi', sayHello: sayHello}
greet1.sayHello() // Hello
greet2.sayHello() // Hi

In the snippet above, we’re borrowing a reference to the sayHello function and setting its reference directly on the objects greet1 and greet2. In so doing, we can implicitly call sayHello in the context of any of the objects. JavaScript will simply call the sayHello function and set its this keyword equal to the context object used to invoke the function.

It decides what the this keyword will point at based on what object is used to invoke the sayHello function. Looking at both call sites in the example above, we can tell that the this keyword will be equal to the object in front of the function call. So greet2.sayHello() says invoke the function sayHello with its this keyword pointing at the greet2 object.

Consider this object containing a function:

const myObj = {
  greet: 'Hello',
  func: function sayHello() {
    console.log(this.greet)
  },
}
myObj.func() // Hello

We have an object that contains a property that holds a function. This property is also known as a method. Whenever this method is called, its this keyword will be bound to its immediate enclosing object—myObj. This is true for both strict and non-strict modes.

Explicit Binding

Functions in JavaScript are considered first-class objects, which means that they can be stored in variables, passed around, returned from other functions, and even hold their properties. All functions descend from the built-in Function object and they inherit methods defined on the Function.prototype object.

We can use two of its methods, call() and apply(), to invoke a function in different contexts or with different context objects. These methods execute the function pointing to the provided object context as the value of this.

Consider the following example:

function sayHello() {
  console.log(this.greet)
}
var greet1 = {
  greet: 'Hello',
}
sayHello.call(greet1) // Hello
sayHello.apply(greet1) // Hello

In the example above, we’re invoking the sayHello function using the call() and apply() methods and they both accept an object—greet1 as their first argument. Explicitly invoking the sayHello function with these methods will force the function to use the object greet1 for its this binding in both cases.

Both call() and apply() behave identically and will set greet1 as the value of this inside the sayHello function. Still, the difference between the two methods is how they handle additional parameters, but we won’t cover that in this article.

In non-strict mode, if you pass a primitive value as the “this” binding, the primitive value will be coerced to its object-wrapped form.

Variation of Explicit Binding

Unfortunately, there is a possibility of losing the intended this binding or having it set to the global object when passing functions around or providing a callback to another function. The function method bind() is a utility built into JavaScript, and it was added in ES5 to set the value of a function’s this regardless of how the function is called.

Consider the following example:

var greet = 'Hi'
function sayHello(cb) {
  cb()
}
var obj = {
  greet: 'Hello',
  greeting: function () {
    console.log(this.greet)
  },
}
sayHello(obj.greeting) // Hi
sayHello(obj.greeting.bind(obj)) // Hello

In the example above, when we pass obj.greeting as a callback to the function sayHello(), its this keyword loses its this binding to obj and points to the global object. this.greet is not referring to obj—instead it is referring to the global object. This can be solved by using the bind() method to set the value of this to obj.

bind() doesn’t invoke the function immediately. It returns a function with “this” bound to the object it receives as its first argument.

The ‘New’ Keyword

Invoking a function with the new keyword is referred to as a constructor call. When a function is called with the new keyword in front of it, it does four things:

  1. It creates a brand new empty object.
  2. The newly created object is linked to the function’s prototype object.
  3. The function is invoked with its this keyword pointing to the new object
  4. If the function doesn’t return an object, it implies a return this. With the this keyword already pointing to the newly created object, the newly created object is returned automatically.

Consider the following:

function sayHello(greet) {
  this.greet = greet
}
let me = new sayHello('Hello Ifeoma')
console.log(me.greet) // Hello Ifeoma

In the example above, calling the function sayHello with the new keyword would immediately create a new object that is an instance of the function sayHello. The this binding inside the function body points to the new object that was created and assigned to the variable me.

Default Binding

When you use this inside a function that is invoked without setting the call to any context object, by default, this will point to the global object, which is the window in a browser. The default binding is applied when a function is called the regular way (e.g., sayHello()).

Consider the following:

var greet = 'Hello'
function sayHello() {
  // not in strict mode
  console.log(this)
  console.log(this.greet)
}
sayHello() // Window
// Hello

In the example above, when we call our sayHello function, this.greet resolves to our global variable greet because variables defined in the global scope are global object properties. We called sayHello without setting any context object, so the default binding applies here if the function is not in strict mode.

var greet = 'Hello'
function sayHello() {
  // not in strict mode
  'use strict'
  console.log(this)
  console.log(this.greet)
}
sayHello() // undefined
// TypeError: Cannot read property 'greet' of undefined

In the example above, the content of our function is running in strict mode, so the global object is not eligible for the default binding in this case. The value of this, in this case, is set to undefined.

Arrow Functions

Arrow functions, by default, do not define a this keyword, meaning that they do not have their own this binding. If you define a this keyword within an arrow function, it’s no different from declaring a regular variable in JavaScript. It will lexically resolve to some enclosing scope that does define a this keyword or the global scope.

To explain how this works with regard to the arrow functions, consider the following:

let obj = {
  greet: 'hello',
  func: function sayHello() {
    return () => {
      console.log(this.greet)
    }
  },
}
let a = obj.func()
a() // Hello

In the example above, the this keyword in the arrow function inside our sayHello function refers to the value of this in the arrow function environment (where it was defined). In our case, it’ll look up the scope chain to see if its parent scope has a this keyword; since the enclosing scope containing the sayHello function is an object, the value of this in the arrow function points to the object.

Order of Precedence

What is the order of precedence if more than one rule matches a call site?

  • If the function was called with the new keyword, this would point to the newly created object.
  • If it was called with any of these—call(), apply() or bind()—use the specified context object as the value of this.
  • If it was called with a context object, the value of this would point to that object.
  • Finally, default to the global object if any of the rules above don’t match and the function isn’t running in strict mode.

Ifeoma-Imoh
About the Author

Ifeoma Imoh

Ifeoma Imoh is a software developer and technical writer who is in love with all things JavaScript. Find her on Twitter or YouTube.

Related Posts

Comments

Comments are disabled in preview mode.