🌟 Mastering call(), apply(), and bind() in JavaScript – A Beginner's Guide

🌟 Mastering call(), apply(), and bind() in JavaScript – A Beginner's Guide

Β·

5 min read

Unlocking the Power of call(), apply(), bind(), this, and Higher-Order Functions in JavaScript πŸ”“πŸš€

Hey there, JavaScript explorer! πŸ‘‹ Let’s dive into three super handy methods: call(), apply(), and bind(), while also unlocking the secrets of the mysterious this keyword. Along the way, we’ll look at Higher-Order Functions (HOF) and their magic. Ready? Let’s go! πŸš€

What is the this Keyword? πŸ€”

The this keyword in JavaScript refers to the object that’s currently calling the function. But it’s a bit tricky because its value changes depending on how a function is called.

A Quick Example:

const person = {
  name: 'Hassani',
  greet: function () {
    console.log(`Hello, my name is ${this.name}!`);
  },
};

person.greet(); // Hello, my name is Hassani!

Here, this refers to person. But if you pull the function out and call it separately, things get messy:

const greetFunc = person.greet;
greetFunc(); // Uh-oh! 'this' is undefined (or the global object in non-strict mode)

Arrow Functions and this

Arrow functions don’t have their own this. Instead, they inherit this from the surrounding scope.

const obj = {
  name: 'Hassani',

  // Method
  greet: () => {
    console.log(this.name); // undefined because 'this' refers to the outer scope
  },
};
obj.greet();

Important Note for Event Listeners:

In regular functions, this inside an event listener points to the element the event is attached to.

document.querySelector('button').addEventListener('click', function () {
  console.log(this); // The button element
});

But if you use an arrow function, it won’t have its own this. Instead, it inherits this from its surrounding scope:

document.querySelector('button').addEventListener('click', () => {
  console.log(this); // The global object or undefined in strict mode
});

Why Do We Need call(), apply(), and bind()?πŸ› οΈ

Sometimes, we want to:

  1. Manually set this to a specific object (e.g., borrow methods from one object for another).

  2. Pass arguments dynamically to functions.

  3. Control this behavior in event listeners, callbacks, or returned functions.

These methods give us explicit control over this, preventing unpredictable bugs. πŸŽ‰

What is a Higher-Order Function (HOF)? πŸ€“

A Higher-Order Function is a function that:

  1. Accepts another function as an argument, OR

  2. Returns a new function.

Why are HOFs useful?

They make our code flexible, reusable, and easier to read. Examples include map(), filter(), reduce(), and methods like bind().

Practical Example of an HOF:

function multiplyBy(multiplier) {
  return function (num) {
    return num * multiplier;
  };
}

const double = multiplyBy(2);
console.log(double(5)); // 10

Here, multiplyBy returns a function, making it a classic HOF!

The Heroic Trio: call(), apply(), and bind() 🦸

These methods live on the Function prototype, so every function in JavaScript automatically inherits them.

call() πŸ“ž

The call() method executes a function immediately and allows you to:

  1. Set this to a specific object.

  2. Pass arguments one by one.

Syntax:

func.call(thisArg, arg1, arg2, ...);
  • thisArg: The object you want this to point to.

  • arg1, arg2, ...: Arguments for the function.

Example: Borrow a Method

const user1 = { name: 'Alice' };
const user2 = { name: 'Bob' };

function introduce() {
  console.log(`Hi, I am ${this.name}!`);
}

introduce.call(user1); // Hi, I am Alice!
introduce.call(user2); // Hi, I am Bob!

What Does call() Return?

It returns the result of the function.

function add(a, b) {
  return a + b;
}

console.log(add.call(null, 2, 3)); // 5

apply() 🀲

The apply() method is similar to call(), but it takes arguments as an array.

Syntax:

func.apply(thisArg, [arg1, arg2, ...]);

Example: Borrow a Method with Arguments

function sum(a, b, c) {
  return a + b + c;
}

console.log(sum.apply(null, [1, 2, 3])); // 6

Why is apply() Old-School?

Before modern JavaScript (... spread operator), apply() was often used to pass arrays as arguments. Today, the spread operator makes call() just as effective:

sum.call(null, ...[1, 2, 3]); // 6

What Does apply() Return?

Like call(), it returns the result of the function.

bind() 🎯

The bind() method doesn’t execute the function immediately. Instead, it returns a new function with this permanently set to the specified object.

Syntax:

const boundFunc = func.bind(thisArg, arg1, arg2, ...);

Example 1: Permanently Set this

const car = {
  brand: 'Toyota',
  getBrand: function () {
    return this.brand;
  },
};

const getCarBrand = car.getBrand.bind(car);
console.log(getCarBrand()); // Toyota

Example 2: Use bind() in Event Listeners

const button = document.querySelector('button');
const user = {
  name: 'Hassani',
  greet: function () {
    console.log(`Hello, ${this.name}!`);
  },
};

// Binding 'this' to 'user'
button.addEventListener('click', user.greet.bind(user));

What Does bind() Return?

It returns a new function with this set to the specified object.

const multiply = function (a, b) {
  return a * b;
};

const multiplyByTwo = multiply.bind(null, 2);
console.log(multiplyByTwo(5)); // 10

Key Differences and Summary πŸ“Š

MethodExecutes Immediately?Arguments FormatReturns
call()YesComma-separatedFunction result
apply()YesArrayFunction result
bind()NoComma-separatedNew bound function

And that’s a wrap! 🎁 You’re now a pro at call(), apply(), and bind()! Got questions or examples? Tweet me @hassani – let’s chat JavaScript! πŸš€πŸ’¬

Happy coding! πŸ’»βœ¨

Β