4. Call, Apply, And Bind In JavaScript

Huabin ZhangHuabin Zhang
4 min read

4.1 Why Manually Change the this Value in a Function

Because the value of this is dynamic, depending on how the function is called, rather than being static. Here are a few common use cases that explain why we need to manually adjust the value of this:

1 this in Event Handler Functions

In event handler functions, this by default refers to the DOM element that triggered the event, not the object we defined. We may want to access our own object inside the event handler instead of the DOM element, and in that case, we need to manually adjust the value of this.

const button = document.querySelector('button');
const person = {
  name: 'Alice',
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

button.addEventListener('click', person.greet);  // `this` refers to the button element

2 Function Reusability and Borrowing

Sometimes, we need to borrow methods from other objects or reuse a method to handle different objects. In such cases, if we don't manually set this, the function's behavior may not work as expected.

const person = {
  name: 'Bob',
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

const animal = {
  name: 'Cat'
};

// Calling greet directly will cause this to point incorrectly.
person.greet();  // Output:Hello, I'm Bob

const greetAnimal = person.greet;
greetAnimal();  // Output:Hello, I'm undefined(`this` do not point to person)

Here, when greetAnimal() is called, this by default refers to the global object (which is window in the browser, or undefined in strict mode).

3 this in Timers and Callback Functions

When using setTimeout or other asynchronous operations, this usually doesn't refer to the object you expect, because the callback function is invoked in a different execution context.

const obj = {
  name: 'Alice',
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

setTimeout(obj.greet, 1000);  // Output:Hello, I'm undefined

4 this in Class Instances

In a class, the this keyword inside a method by default refers to the class instance. However, if you pass a class method to an external function and want to retain the reference to the instance, you must manually bind this.

class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const person = new Person('Bob');
const greetPerson = person.greet;
greetPerson();  // Output:Hello, I'm undefined(`this` do not refer to the person instance)

4.2 Manually set this using call, apply, and bind

call, apply, and bind are three commonly used function methods in JavaScript, used to manually change the value of this inside a function.

MethodsWhether to execute immediatelyParameter formatReturn value
call“ this “, Single parameter or multiple parametersExecution result
apply“ this “, Single array parameterExecution result
bind“ this “, Single parameter or multiple parametersReturn a new function

4.3 Real-world analogy for call, apply, and bind

Imagine you're a director, and there's an "actor" (a function) who can wear different "costumes" (the this context). If you want the actor to wear someone else's costume, you can use one of three methods — call, apply, or bind — to change their appearance.

Character setup

function sayHello(greeting, ending) { // "actor"
  console.log(`${greeting}, I'm ${this.name} ${ending}`);
}

const alice = { name: "Alice" }; // costume Alice
const bob = { name: "Bob" }; // costum Bob

call Method

The director demands an immediate performance — the actor puts on the Alice costume and starts acting right away!

sayHello.call(alice, "Hi", "!");
// Output:Hi, I'm Alice !

apply Method

The director demands an immediate performance, but the parameters are handed to the actor in a single "package".

sayHello.apply(bob, ["Hello", "!!!"]);
// Output:Hello, I'm Bob !!!

bind Method

The director tells the actor to get into costume, but not to perform yet — wait until the cue, “Action!”, before starting.

const bobSayHello = sayHello.bind(bob, "Hey", "!!!");
// No output

bobSayHello(); // Output:Hey, I'm Bob !!!

4.4 Implementing A Custom bind Function

  • first we need to store the original function that calls bind function

  • return a new function that can ensure that the new function this refers to the context we pass in

  • the arguments of bind function and new function can be combined

Function.prototype.myBind = function(context, ...args) {
    // store the original function, this refers to the function that calls myBind 
    const originalFunction = this;

    // return a new function
    return function(...newArgs) {
        // call the original function with the context and combined arguments
        return originalFunction.apply(context, [...args, ...newArgs]);
    }
}

function Person(greeting, ending) {
    name = 'DuDu';
    console.log(`${greeting}, I'm ${this.name} ${ending}`);
}
const Alice = {name: 'Alice'};
const NewPerson = Person.myBind(Alice, 'Hello');
NewPerson('Bye!'); // Hello, I'm Alice Bye!
0
Subscribe to my newsletter

Read articles from Huabin Zhang directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Huabin Zhang
Huabin Zhang