Grokking call(), apply() and bind() methods in JavaScript

Grokking call(), apply() and bind() methods in JavaScript

These functions are very important for every JavaScript Developer and are used in almost every JavaScript Library or Framework. Check out the code snippet below.

Taken from the very popular library Lodash

/**
* Creates a function that invokes `func` with arguments reversed.
*
* @since 4.0.0
* @category Function
* @param {Function} func The function to flip arguments for.
* @returns {Function} Returns the new flipped function.
* @see reverse
* @example
*
* const flipped = flip((...args) => args)
*
* flipped('a', 'b', 'c', 'd')
* // => ['d', 'c', 'b', 'a']
*/
function flip(func) {
  if (typeof func !== 'function') {
    throw new TypeError('Expected a function')
  }
  return function(...args) {
    return func.apply(this, args.reverse())
  }
}

export default flip

Look at the statement on line 21, return func.apply(this, args.reverse())

In this article, we will have a look at the call(), apply() and bind() methods of JavaScript. Basically these 3 methods are used to control the invocation of the function. The call() and apply() were introduced in ECMAScript 3 while bind() was added as a part of ECMAScript 5.

Let us start with an example to understand these.

Suppose you are a student of X university and your professor has asked you to create a math library, for an assignment, which calculates the area of a circle.

const calcArea = {
  pi: 3.14,
  area: function(r) {
    return this.pi * r * r;
  }
}

calcArea.area(4); // prints 50.24

You test this and verify its result, it is working fine and you upload the library to portal way before the deadline ends. Then you ask your classmates to test and verify as well, you come to know that that your result and their results mismatches the decimals precision. You check the assignment guidelines, Oh no! The professor asked you to use a constant pi with 5 decimals precision. But you used 3.14 and not 3.14159 as the value of pi. Now you cannot re-upload the library as the deadline date was already over. In this situation, call() function will save you.

Using call() or Function.prototype.call()

calcArea.area.call({pi: 3.14159}, 4)

As you can see, it takes in a new object with new pi value. And now the result will be

50.26544

Let us breakdown what is happening here. If you observe the call(), it takes in two arguments

  • Context (object)
  • Arguments

A context is an object that replaces the this keyword in the area function. And then arguments are passed as function arguments.

const cylinder = {
    pi: 3.14,
    volume: function(r, h) {
        return this.pi * r * r * h;
    }
};

Call invocation

cylinder.volume.call({pi: 3.14159}, 2, 4);
// 50.26544

Using apply() or Function.prototype.apply()

Apply is exactly the same, except function arguments are passed as an array or you can use an Array object (new Array(2, 4)).

cylinder.volume.apply({pi: 3.14159}, [2, 4]);
// 50.26544

Using bind() or Function.prototype.bind()

It allows us to input context into a function which returns a new function with an updated context. Basically it means bind() attaches a new this to a given function. Unlike call() and apply(), bind() function is not executed immediately but later when required. bind() is useful while working with JavaScript events.

Summary

  • All the three functions have one similarity, their first argument is always the 'this' value or context, that you want to pass to the function you call the method on.
  • call() and apply() are invoked immediately, bind() returns a bound function, with the context, which will be invoked later.
  • call() and apply() are similar the only difference being, arguments in apply() is passed as an array.

Read more on MDN call() , apply() and bind()

Let me know in the comments, if you have used these methods in your projects.