The function's this keyword

Introduction to this

In JavaScript, functions (except arrow functions) "have" an own this that generally refers to an object outside the function. The value that this represents in a function is determined at run-time by how the function is called. The value may be different in different function calls. This unbound behavior of this in JavaScript is different from the behavior of this (or similar) keyword in other languages.


"use strict";

function getThis() {
  return this;
}

console.log(getThis()); // logs: undefined

console.log(getThis.call({number: 313})); // logs: { number: 313 }
console.log(getThis.call({string: "Hello World"})); // logs: { string: "Hello World" }

PS: The use of method call() will be explained later in this chapter.

Function context

this may appear in a function (function context), in global scope (global context, see next section), or in a class (class context, covered in a later chapter).

The common use of this in functions is when those functions are to act as methods of objects. A method is called on an object, like object.method(), setting this in the method to the object it is called on. Generally this refers to an object. In the next example this.firstName refers to property firstName of object name because the method is called as name.fullName() in the last line of the script.

Note that in the example below replacing this by name will create a different method. Keyword this refers to the value, the object, but name refers to the variable. Using the object's identifier instead of this may result in unexpected behavior.


const name = {
  firstName: "John",
  lastName: "Doe",  
  fullName: function() {
    return this.firstName+" "+this.lastName;
  }
};

console.log(name.fullName()); // logs: "John Doe"

const name = {
  firstName: "John",
  lastName: "Doe",  
  fullName: function() {
    return name.firstName+" "+name.lastName; // 'name' instead of 'this'
  }
};

// create a copy of 'name' and name it 'user': 
const user = Object.assign({}, name);

user.firstName = "Jane";
console.log(user.firstName); // logs: "Jane"
console.log(user.fullName()); // logs: "John Doe"

A clearer demonstration that this is bound to the object the function is called on:


const father = {
  firstName: "John",
  lastName: "Doe"
};

const daughter = {
  firstName: "Mary Jane",
  lastName: "Doe"
};

function salutation() {
  return "Dear "+this.firstName+" "+this.lastName;
}

father.greeting = salutation;
daughter.greeting = salutation;

// Same function, but 'this' means different things due to calls on different objects:
console.log(father.greeting()); // logs: "Dear John Doe"
console.log(daughter.greeting()); // logs: "Dear Mary Jane Doe"

this in a independently called function

Keyword this used inside a function that is not called as a method but independently, has the value undefined in strict mode. In non-strict mode the value of this is the global object (see next section). Calling functions containing this independently (without referring to a specific object) should be avoided.


 // In non-strict mode:
function f1() {
  return this;
}

console.log(f1() === window); // logs: true ('window' is the global object in a browser)

 // In strict mode:
function f2() {
  'use strict';
  return this;
}

console.log(f2() === undefined); // logs: true
console.log(window.f2() === window); // logs: true // (function called as a method)

Global context & global object

Keyword this can also be used outside any function, directly in global scope, or more precisely, in the global execution context, i.e., outside of any functions or classes, but possibly inside a block statement or arrow function defined in the global scope. In this global context this always refers to the global object.

The global object is a built-in object that always exists in the global scope (the top level scope). In client-side JavaScript, the global scope is generally the web page inside which all the code is being executed. The global object has a number of standard built-in properties and methods that are always available in global scope. Other objects in the global scope are either explicitly created in global scope or provided by web APIs. var variable declarations and function declarations in global scope explicitly create properties of the global object. However, explicitly creating global object properties or methods is generally considered something that should be kept to a minimum. let variables and const constants can be available in global scope (by declaration in global scope), but do not become properties of the global object.

In a browser the global object is named window. In other environments the global object may have another name, like global in Node.js. globalThis can be used as a standardized reference to the global object.


console.log(this === window); // logs: true // (in a browser)
console.log(this === globalThis); // logs: true

Global object's properties and methods may be referenced or called directly (without a reference to the global object) or specifically as a global object's property or method (globalThis.a):


var a = 2;
let b = 3;
const myObj = { a: 2 }
function f() {
  console.log(a === this.a)
}

console.log(a === globalThis.a); // logs: true
console.log(a === this.a); // logs: true
globalThis.f(); // logs: true
f(); // logs: true // in strict mode it returns "TypeError: this is undefined"
console.log(b === globalThis.b); // logs: false
console.log(myObj === globalThis.myObj); // logs: false

console.log(undefined === globalThis.undefined); // logs: true
console.log(isNaN() === globalThis.isNaN()); // logs: true
console.log(Number() === globalThis.Number()); // logs: true

this as an event handler

An event handler is a function that is called when an event fires. An event is an action or occurrence that happens in the application, which fires a signal that code can react to. Typically events are initiated by user actions, like key strokes, mouse clicks and hovers or screen swipes.

this in an event handler is set to the mark-up element (<button>, <a> etc.) on which the listener is placed. In the example below function changeColor acts as a listener/event handler for the click event on each button. The value of this in function changeColor is different for each event (for each click): it is set to the button on which the function changeColor is placed in each iteration of the for loop.


<ul id="myButtons">
  <li><button>Click me</button></li>
  <li><button>Click me</button></li>
  <li><button>Click me</button></li>
</ul>
<script>
function changeColor(e) {
  this.style.backgroundColor = "hsl("+360 * Math.random()+", 100%, 70%)";
}

const buttons = document.querySelectorAll("#myButtons > li > button"); 
for (const button of buttons) {
  button.addEventListener('click', changeColor, false);
}	
</script>

Be careful with nested functions within the event handler. this within such inner functions have lost binding with the element on which the listener is placed. As described before, this inside a function that is not called as a method has, in strict mode, the value undefined, and in non-strict mode the value is the global object. An often used solution is to declare a variable in top scope of the event handler and assign it the value of this. Next example shows how this works. A modern solution is to use arrow functions or to use bind(), both explained further below.


<ul id="myButtons">
  <li><button>Click me</button></li>
  <li><button>Click me</button></li>
  <li><button>Click me</button></li>
</ul>
<script>
const buttons = document.querySelectorAll("#myButtons > li > button");
buttons.forEach(function(button) {
  button.addEventListener('click', function() {
    const _this = this; // preserve "this" for inner functions   
    setTimeout(function() {
	  // "this" is the global object (in non-strict mode) in this inner function
      _this.style.backgroundColor = "hsl("+360 * Math.random()+", 100%, 70%)"; // "this.style" is undefined and returns an error
    }, 1000);
  });
});
</script>

Click a button and after 1 second its color changes:

call() and apply()

We can use the built-in function methods call() and apply() to explicitly set the value of this when calling the function. With these methods you can write a function (method) once and then use it on multiple objects, without having to rewrite the method multiple times.


const person = {
  fullName: function() {
    return this.firstName + " " + this.lastName;
  }
}

const users = [
  { firstName: 'Janeth', lastName: 'Jacobs' },
  { firstName: 'Jake', lastName: 'Jansen' }
];

console.log(person.fullName.call(users[0])); // logs: Janeth Jacobs
console.log(person.fullName.apply(users[1])); // logs: Jake Jansen

Next example is a script used in an earlier example, except now call() and apply() are used to set the value of this. In this and the above example both call() and apply() do the same thing.


const father = {
  firstName: "John",
  lastName: "Doe"
};

const daughter = {
  firstName: "Mary Jane",
  lastName: "Doe"
};

function salutation() {
  return "Dear "+this.firstName+" "+this.lastName;
}

console.log(salutation.call(father)); // logs: "Dear John Doe"
console.log(salutation.call(daughter)); // logs: "Dear Mary Jane Doe"

console.log(salutation.apply(father)); // logs: "Dear John Doe"
console.log(salutation.apply(daughter)); // logs: "Dear Mary Jane Doe"

The first parameter of the call() and apply() method represents the value to use as this when calling the function. Optionally you can add arguments for the function. call() accepts an argument list, while apply() requires a single array of arguments. This is the only difference between call() and apply(). The apply() method can be very handy when arguments are in an array.


// next two statement do exactly the same as
// console.log(Math.pow(2, 3));
console.log(Math.pow.call(null, 2, 3)); // logs: 8
console.log(Math.pow.apply(null, [2, 3])); // logs: 8

const numbers = [2, 7, 3, 5, 9];
let max = Math.max.apply(null, numbers);
console.log(max); // logs: 9

// or with spread syntax:
max = Math.max(...numbers);
console.log(max); // logs: 9

Above the this keyword is not involved, hence the use of null (which could have been any other valid value). See also "this always set to an object?".

Examples


const users = [
  { firstName: 'Janeth', lastName: 'Jacobs' },
  { firstName: 'Jake', lastName: 'Jansen' }
];

const print = function (i) {
  console.log(`#${i}: ${this.firstName} ${this.lastName}`);
}  

for (let i = 0; i < users.length; i++) {
  print.call(users[i], i);
}

// logs:
// #0: Janeth Jacobs
// #1: Jake Jansen

const kindWords = [ 'Hello', 'How was your day', 'Can I do something for you' ];
const users = [
  { honorific: 'Mrs', lastName: 'Jacobs' },
  { honorific: 'Mr', lastName: 'Jansen' }
];

const print = function (...firstWords) {
  for (let i = 0; i < firstWords.length; i++) {
    console.log(`${firstWords[i]} ${this.honorific} ${this.lastName}?`);
  }
}

for (let i = 0; i < users.length; i++) {
  print.apply(users[i], kindWords);
}

// logs: 
// Hello Mrs Jacobs?
// How was your day Mrs Jacobs?
// Can I do something for you Mrs Jacobs?
// Hello Mr Jansen?
// How was your day Mr Jansen?
// Can I do something for you Mr Jansen?

this always set to an object?

Technically call() and apply() can also set this to a primitive value (a non-object). In non-strict mode call() and apply() will always convert primitive values to an object; null and undefined will be set to the global object and other primitives (boolean, string, number, BigInt) will be set to an instance of their primitive wrapper object. In strict mode no conversion happens.


let foo = 2;

function bar() {
  return this;
}

function baz() {
  'use strict';
  return this;
}

console.log(bar.call(foo)); // logs: Number { 2 }
console.log(baz.call(foo)); // logs: 2

foo = null;
console.log(bar.call(foo)); // logs: window // (the global object in a browser)
console.log(baz.call(foo)); // logs: null

foo = undefined;
console.log(bar.call(foo)); // logs: window
console.log(baz.call(foo)); // logs: undefined

console.log(bar.call()); // logs: window
console.log(baz.call()); // logs: undefined

bind()

Calling someFunction.bind(someObject) creates a new function with the same body and scope as someFunction, but with this permanently bound to the first argument of bind(), regardless of how the function is being called.


function someFunction() {
  return this.a;
}

const newFunction = someFunction.bind({ a: 'foo' });
console.log(newFunction()); // logs: foo

const newObject = { a: 313, someFunction, newFunction };
console.log(newObject.a, newObject.someFunction(), newObject.newFunction());
// logs: 313, 313, foo

Optionally you can add arguments to set parameters of the original function in the new function. They will be inserted and preset at the start of the parameters, moving the rest of the parameters to the right.


function myFunction(x, y){ console.log(x, y) }
const newFunction = myFunction.bind(null, 10);

newFunction(2, 3) // logs: 10 2 // (the second argument, 3, is ignored)

function myFunction(...args){ console.log(args) }
const newFunction = myFunction.bind(null, 10);

newFunction(2, 3) // logs: [ 10, 2, 3 ]

Examples


const person = {
  fullName: function() {
    return this.firstName + " " + this.lastName;
  }
}

const users = [
  { firstName: 'Janeth', lastName: 'Jacobs' },
  { firstName: 'Jake', lastName: 'Jansen' }
];

// add bound methods 'fullName' to the users objects:
users[0].fullName = person.fullName.bind(users[0]);
users[1].fullName = person.fullName.bind(users[1]);

console.log(users[0].fullName()); // logs: Janeth Jacobs
console.log(users[1].fullName()); // logs: Jake Jansen

users[0].firstName = "Joyce";
console.log(users[0].fullName()); // logs: Joyce Jacobs

const myObject = {
  value: 121,
  returnValue: function() {
    return this.value;
  }
};

// extract a method from an object:

const extractedMethodUnbound = myObject.returnValue;
console.log(extractedMethodUnbound()); // logs: undefined // The function gets invoked in global scope

const extractedMethodBound = myObject.returnValue.bind(myObject);
console.log(extractedMethodBound()); // logs: 121

function addArguments(arg1, arg2) {
  return arg1 + arg2;
}

// Create a new function with a preset first argument:
const addTen = addArguments.bind(null, 10);

console.log(addTen(5)); // logs: 15 // 10 + 5 = 15

console.log(addTen(5, 10)); // logs: 15 // 10 + 5 = 15 (the second argument is ignored)

<ul id="myButtons">
  <li><button>Click me</button></li>
  <li><button>Click me</button></li>
  <li><button>Click me</button></li>
</ul>
<script>
const buttons = document.querySelectorAll("#myButtons > li > button");
buttons.forEach(function(button) {
  button.addEventListener('click', function() {
    setTimeout( (function() {
	  this.style.backgroundColor = "hsl("+360 * Math.random()+", 100%, 70%)";
    }).bind(button), 1000);
  });
});
</script>

Click a button and after 1 second its color changes:

this in arrow functions

Arrow functions do not have their own this. The value of this in an arrow function is the same value of this right outside the arrow function, regardless of how the arrow function is called. In other words, the arrow function has no effect on the value of this. Arrow functions are generally not suitable to use as object methods.

In the next example the first this in object name is not in any function, so its value is global scope. Method fullName does not bind an other value to this: also this in the arrow function refers to the global object.


const name = {
  valueOfThis: this,
  firstName: "John",
  lastName: "Doe",  
  fullName: () => { return this.firstName+" "+this.lastName; }
};

console.log(name.valueOfThis); // logs: window // (in a browser)
console.log(name.fullName()); // logs: "undefined undefined"

Arrow functions with this can be very useful as nested functions within an event handler. Earlier we saw that this within nested function lose binding with the element on which the listener is placed, but with arrow functions this is not the case. this in the arrow function retains the value of this in the enclosing scope.


<ul id="myButtons">
  <li><button>Click me</button></li>
  <li><button>Click me</button></li>
  <li><button>Click me</button></li>
</ul>
<script>
const buttons = document.querySelectorAll("#myButtons > li > button");
buttons.forEach(function(button) {
  button.addEventListener('click', function() {
    setTimeout(() => {
      this.style.backgroundColor = "hsl("+360 * Math.random()+", 100%, 70%)";
    }, 1000);
  });
});
</script>

Click a button and after 1 second its color changes:

The call, apply and bind methods cannot be used to set an arrow function's this, since an arrow function does not have its own this. The first argument is simply ignored (and should be set to null when you use these methods on an arrow function).


var a = "global variable"
const foo = (() => this.a);
const obj = { a: "object property" };

console.log(foo.call(obj)); // logs: "global variable"
console.log(foo.bind(obj)()); // logs: "global variable"

const bar = function() { return this.a }

console.log(bar.call(obj)); // logs: "object property"
console.log(bar.bind(obj)()); // logs: "object property"