JavaScript 101 - call(), apply() & bind()

JavaScript 101 - call(), apply() & bind()

Understanding JavaScript the simple way.

Indrajeet Nikam's photo
Indrajeet Nikam

Published on Aug 25, 2021

6 min read

Subscribe to my newsletter and never miss my upcoming articles

"JavaScript works in mysterious ways!"

I always thought about it that way. Until recently, when I decided to explore its weird behaviour and understand why it is like that.

Today, I want to share my understanding of one such mystery.

i.e. call(), apply() & bind()

What do they do? How you can use them?

Let's try to answer these questions in a simple way with examples.

Before we start...

You might wanna read a little bit about Object-Oriented JavaScript. It will make much more sense after that ๐Ÿ˜‰

Here is simple explanation. For detailed explanation refer these docs.

Let's have a look

Let's go over functionality of each function,

call()

  • Syntax

      call()
      call(thisArg)
      call(thisArg, arg1)
      call(thisArg, arg1, ... , argN)
    
  • Parameters

    1. thisArg optional - Value to be used as this inside function.
    2. arg1, ..., argN optional - Arguments to the function in proper order.
  • Returns

    whatever function returns.

  • Description

    call() allows the function of one object to be assigned & called for another object.

    You can use it like this,

      <function>.call(<object>, arguments...);
    
  • Applications

    1. func.call() is similar to func() -

       // Add function
       function add(num1, num2) {
           return num1 + num2;
       }
      
       // Both syntaxt below are valid for invoking above func.
       add(10, 20)
       // ๐Ÿ‘†๐Ÿผ normal way
      
       add.call(this, 10, 20)
       // ๐Ÿ‘†๐Ÿผ call() way. Here this will be window obj. If you are using
       // strict mode, this will be undefined
      
    2. Function borrowing -

       const somebody = {
           name: 'somebody',
           sayHi: function() {
               console.log('Hi, ', this.name);
           },
           sayHello: function() {
               console.log('Hello, ', this.name);
           }
       }
      
       const indrajeet = {
           name: 'Indra',
         sayNamaste: function() {
               console.log('Namaste, ', this.name);
           }
       }
      
       // We can borrow functions from other objects like below
       somebody.sayHi.call(indrajeet);
       // returns -> "Hi, Indra"
      
       indrajeet.sayNamaste.call(somebody);
       // returns -> "Namaste, somebody"
      
       // what happened here?
       //   When we are passing the first argument as an object for reference. "this" is
       //   replaced by the provided object, making name available in another object.
      
    3. Constructor chaining -

      It is a bit advance for this article but feel free to know more about it here.

apply() - the same one!

Spoiler alert! It's the same as call ๐Ÿ˜‘ (with a slight change of course)

  • Syntax

      apply(thisArg)
      apply(thisArg, argsArray)
    
  • Parameters

    1. thisArg required - Value to be used as this inside the function. Although required, you can use null instead ๐Ÿ˜…
    2. argsArray optional - Array of arguments for the function.
  • Returns

    whatever function returns.

  • Description

    apply() also allows the function of one object to be assigned & called for another object.

    You can use it like this,

      <function>.apply(<object>, [argumentsArray]);
    
  • Applications

    1. func.apply(null) is also similar to func() -

      It's pretty much the same as call(), except for the fact that it accepts argument array as the second parameter. Instead of array-like object in case of call()

       // Add function
       function greet() {
           return 'Hellow ๐Ÿ‘‹๐Ÿผ';
       }
      
       // Both syntaxt below are valid for invoking above func.
       greet()
       // ๐Ÿ‘†๐Ÿผ normal way
      
       greet.apply(this)
       // ๐Ÿ‘†๐Ÿผ apply() way. Here this will be window obj. If you are using
       // strict mode, this will be undefined
      
    2. Append one array to another -

      passing another array directly using push() creates array inside array. To avoid that, you can use apply()

       const array1 = ['a', 'b'];
       const array2 = ['c', 'd'];
      
       array1.push.apply(array1, array2);
       console.log(array1);
       // result -> ["a", "b", "c", "d"]
      
    3. Function borrowing -

      Just like call(), it can also do the function borrowing magic.

    4. Constructor chaining

      It is a bit advance for this article but feel free to know more about it here.

bind() - the different one!

At last, something that's not doing the same thing ๐Ÿ˜…

bind() is very unique and lets you do some amazing things.

This is the most tricky bit, let's be patient till we reach examples.

  • Syntax

      bind(thisArg)
      bind(thisArg, arg1)
      bind(thisArg, arg1, ... , argN)
    
  • Parameters

    1. thisArg required - Value to be used as this to target function when the function is called later. If you pass null or undefined instead, this of executing scope is treated as thisArg for the new function.
    2. argsArray optional - Arguments to prepend to arguments for the new function when invoking it.
  • Returns

    newly created bound function.

  • Description

    bind() lets you bind an unbound function to the provided object with additional and possibly hidden arguments. You can use bind() to promote reusability. It creates a new bound function! Which usually wraps the original function object and usually abstracts away reusable bits of code.

    You can use it like this,

      <unbound_function>.bind(<object>, arguments...);
    
  • Applications

    1. Creating a bound function -

      To put it simply, once you create a bound function. No matter how it is called afterwards, it will always be called with a particular this value.

      Below is a good example from Docs,

       this.x = 9; // here 'this' refers to global 'window' object here in the browser
       const module = {
         x: 81,
         getX: function() { return this.x; }
       };
      
       module.getX();
       // returns -> 81
      
       const unboundGetX = module.getX; // reference to module is lost here
       unboundGetX();
       // returns -> 9; the function gets invoked at the global scope
      
       // Create a new function with 'this' bound to module
       // New programmers might confuse the
       // global variable 'x' with module's property 'x'
       const boundGetX = unboundGetX.bind(module);
       boundGetX();
       // returns 81
       // now, it will always return 81
      
    2. Partially applied function -

      This is an interesting one! Basically, you can create wrappers over other functions with bind(). Talk about reusability ๐Ÿ˜‰

       function greet(greetings, name) {
           return `${greetings}, ${name}`;
       }
      
       const sayHi = greet.bind(null, 'Hi');
       const sayNamaste = greet.bind(null, 'Namaste')
      
       const result = sayHi('Indra');
       // returns -> "Hi, Indra"
      
       const result2 = sayNamaste('Somebody');
       // returns -> "Namaste, Somebody" 
      
       const result3 = sayNamaste('XYZ', 'Redundant');
       // returns -> "Namaste, XYZ"
       // "Redundant" is ignored, as there is no 3rd parameter in greet func.
      
    3. Explicit bind inside setTimeout() -

      This is also a pretty valid use case. You can explicitly bind this keyword to a class instance in order to maintain the instance. If you don't use bind(), this keyword inside setTimeout is set to window obj.

      Checkout this example from docs,

       function LateBloomer() {
         this.petalCount = Math.floor(Math.random() * 12) + 1;
       }
      
       // Declare bloom after a delay of 1 second
       LateBloomer.prototype.bloom = function() {
         window.setTimeout(this.declare.bind(this), 1000);
       };
      
       LateBloomer.prototype.declare = function() {
         console.log(`I am a beautiful flower with ${this.petalCount} petals!`);
         // If we don't use bind, this keyword will be set to window obj.
         // and hence, will not have access to patelCount prop of LateBloomer
       };
      
       const flower = new LateBloomer();
       flower.bloom();
       // after 1 second, calls 'flower.declare()'
      

Jeez... So much for simple explanation ๐Ÿ˜…

I hope this article will help you understand at least basic use-cases for these three functions.

"I am planning to push more such content regarding the basics of JavaScript in the coming months, so let me know what you feel about that."

If something is wrong or misleading, let me know in the comments below๐Ÿ‘‡๐Ÿผ. I will update it promptly.

If you like the content, please like ๐Ÿ‘๐Ÿผ and share the article. As it would help me reach out to more people.

Did you find this article valuable?

Support Indrajeet Nikam by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
ย 
Share this