Joel's Thoughts

Understanding Apply, Call, and Bind Functions In Javascript

August 07, 2016

I noticed a lot of developers were confused about the difference between these 3 built-in javascript functions ( apply, call, and bind ). This is my attempt to explain the difference between them.

Let’s consider this simple function addIntegers that accepts 2 paramaters (x and y):

    function addIntegers(x, y){
        var total = x - y + this.value;
        return total;
    }

Notice inside the function that it’s using a this keyword. ( If you’re not familiar with the javascript’s this keyword, please read about it here first ).

If we run the addIntegers function:

  let sum = addIntegers(2,3);
  console.log(sum); //output is NaN

The output is NaN since by default the this keyword is equal or pointing to the window global object. And since the window object has no defined value property, this.value is equal to undefined resulting to a sum of NaN.

  //actual operation that happened
  2 + 3 - undefined = NaN

Now let’s create a custom object that will define a value property to use on our addIntegers function.

    function addIntegers(x, y){
        let total = x - y + this.value;
        return total;
    }

    let customObject = new Object(); //create an empty object
    customObject.value = 20; //define a "value" property that is equal to 20 

Calling the addIntegers function:

   let sum = addIntegers(2,3);
   console.log(sum); //output is still NaN

Remember, our addIntegers this keyword is still pointing to the global window so the output is still NaN

So, how do we call our AddIntegers function so that the inner “this” keyword will refer to the customObject we created?

Apply, call, and bind functions to the rescue. Let’s use them.

  1. Using Apply:

     let sum = addIntegers.apply(customObject,[2,3]);
     console.log(sum); //output is 19
    
  2. Using Call:

     let sum = addIntegers.call(customObject,2,3);
     console.log(sum); //output is 19
    
  3. Using Bind:

     let sum = addIntegers.bind(customObject,2,3)();
     console.log(sum); //output is 19
    

To summarize based from the code above:

  1. apply - returns the function’s output with the second parameter as arrays.

    Ex. Function.apply(Object, [param1,param2…])

  2. call - returns the function’s output with the second parameter as normal parameter ( separated by comma if in multiple entries ).

    Ex. Function.call(Object,param1,param2….)

  3. bind - returns a bounded function with the second parameter as normal parameter ( separated by comma if in multiple entries ).

    Ex. Function.bind(Object,param1,param2….)

It is important to take note that bind returns a bounded function and not a value. Also call and apply differs from the way they accept their parameters.

Here’s an example where apply, call and bind can fix an unexpected closure behavior inside for loop.

    function makeArmy() {            
        var shooters = [];                
        for(var i=1; i<11; i++) {                   
            var shooter = function(i) { 
                console.log(i);
            };                    
            shooters.push(shooter);
        }
        return shooters;
    }

Executing the makeArmy function:

    var army = makeArmy();

    var first_shooter = army[0]; // first shooter
    first_shooter(); // expected to be 1 but logs 11

    var fifth_shooter = army[4] // fifth shooter
    fifth_shooter() //expected to be 5 but logs 11

I bet you encountered this unexpected behavior before. The reason is because at the time we called the first_shooter or fifth_shooter function, for loop already finished the looping process. And since a closure remembers or reference the last value of i from its execution context, obviously it will keep using i = 10.

There are a lot of ways to fix this but let’s see how we can use apply, call and bind to solve the problem elegantly.

First let’s use bind:


    function makeArmy() {            
        var shooters = []                
        for(var i=1; i<11; i++) {   

            var shooter = function(param) { 
                console.log(param)
            }.bind(null, i); 

            shooters.push(shooter)        
        }                
        return shooters
    }

As you see, we made i as the second parameter of bind function. This will be automatically mapped to the parameter ( param ) of the shooter function. Also we used null as the first parameter of bind since we don’t care or we don’t use the this keyword reference anyway.

Executing the function will result to the expected behavior:

    var first_shooter = army[0]; // first shooter
    first_shooter(); // logs 1

    var fifth_shooter = army[4] // fifth shooter
    fifth_shooter() // logs 5

Using call, we’ll update the makeArmy function like this:


    function makeArmy() {            
        var shooters = []                
        for(var i=1; i<11; i++) {   

            var shooter = function(param) { 
                return function(){
                         console.log(param)
                       }
            }.call(null, i); 

            shooters.push(shooter)        
        }                
        return shooters
    }

Using apply:


    function makeArmy() {            
        var shooters = []                
        for(var i=1; i<11; i++) {   

            var shooter = function(param) { 
                return function(){
                         console.log(param)
                       }
            }.apply(null, [i]); 

            shooters.push(shooter)        
        }                
        return shooters
    }

Both updates using call and apply will yield expected results:

    var first_shooter = army[0]; // first shooter
    first_shooter(); // logs 1

    var fifth_shooter = army[4] // fifth shooter
    fifth_shooter() // logs 5








  • About
  • Search
  • Resume
  • Powered by Jekyll using the Trio theme