JavaScript’s global scope is like a public toilet. You can’t avoid going in there, but try to limit your contact with surfaces when you do.
— Dmitry Baranovskiy

Note: This article contains an intermediate level topic.  Understanding this material will require knowledge of how the keyword this works in JavaScript, as well as an understanding of how prototype chains and objects work together.

.call(), .apply(), and .bind() - the elevator pitch

Call, apply, and bind are all Function prototype methods.  What does that mean?  Well, all things in JS are objects (including functions), and if we look for a property or a method on an object and it doesn't exist, these failed lookups will be delegated to the Object's prototype.  That means that every function ever created in JS by default will delegate failed lookups to Function.prototype.  Furthermore, Function.prototype cannot be modified.  Here are some quick code examples:

var adder = function (a,b) {
  return a + b;
}

//Because of prototypal lookup, these all work:

//Normal call 
adder(1,2);  // -> 3
//binds 'this' to the first arg passed in.
adder.call(this, 1, 2); // -> 3
//also binds 'this' like .call, but takes an array of arguments 
adder.apply(this, [1,2]); // -> 3

/*
the .bind method is different in that it returns a new function 
whose 'this' keyword is permanently bound to the value passed as the 
first argument to .bind.  Also, .bind can take additional arguments
and will prepend them to the list of arguments passed in when the new
function is called.

In this example, plusFive will take one argument when called, and
will add five to it and return the new value. 
*/

var plusFive = adder.bind(this,5);
plusFive(2); // -> 7 

.call() - a use case

So why would we want to use these function prototype methods?  Well, what if we want to call a function in a context where we do not have access to the scope we want to operate on?  .call and .apply are used in these situations.  For example, we can use .call() to chain object constructors: 

//This function creates a 'vehicle' object
function Vehicle (name, price, VIN) {
  var thisVehicle = {}; 
  this.name = name;
  this.price = price;
  this.VIN = VIN;
}

//This function creates a 'truck' object, which has all
//the same properties as a 'vehicle' object, plus some unique ones

function Truck (name, price, VIN, is4WD) {
  //use the .call() method to set the 'this' binding to local scope 
  Vehicle.call(this, name, price, VIN);
  //Notice the first arg. passed is the 'this' binding, followed by
  //the arguments used by the Vehicle constructor function.
  
  /*
    Note: We could have used .apply() instead like this: 
    Vehicle.apply(this, [name, price, VIN]);
    .apply simply takes an array of arguments rather than passing
    in individual arguments.
  */
  
  this.is4WD = is4WD; 
  this.category = 'Truck';
}

var toyota = new Truck('Tacoma', '$32,525', 'UVHX653-HFD75', true);

console.log(toyota.name); // -> output: 'Tacoma' 

//Below evaluates to true
if (toyota.is4WD) { 
    console.log('Has 4-Wheel Drive!')
}

 

.apply() - Sometimes we don't care about 'this'

the Function.prototype.apply() method works very similarly to the .call() method.  The only difference is that it takes an array of arguments as it's second argument, rather than all arguments after the first being used inside the function we are calling it on.  Sometimes, though, we can use .apply in useful ways when we don't really care about what our 'this' keyword is bound to.  An example would be summing all the values inside an array: 

//this function returns the sum of all it's arguments 
function sumAll () {
  var acc = 0;
  for (var i = 0; i < arguments.length; i++) {
    acc += arguments[i];
  }
  return acc;
}

var backNine = [5,4,3,5,7,7,5,3,5];

/*
Here we call .apply to call our sumAll function passing it 
all elements of our backNine array.  We don't care what this
is bound to since it isn't used inside our function, but we 
are taking advantage of the 'array of arguments' functionality
*/
sumAll.apply(null, backNine);  // -> returns 44

.bind() explained

The .bind() method is quite different from .call and .apply.  Where the former two are used to call a function while explicitly setting it's this binding, .bind actually creates a new function which will have it's this binding permanently set to the first argument passed to .bind:

//global scope- aka 'window' for web browsers
var favoriteColor = 'Red';

var Robot = function (color, quote) {
  this.favoriteColor = color; 
  this.favoriteQuote = quote;
} 

Robot.prototype.myColor = function () {
  return this.favoriteColor;
}

var R2D2 = new Robot('Blue', 'Beep Boop Bop!');

console.log(R2D2.myColor()); // -> Logs 'Blue'
var forDemo = R2D2.myColor.bind(window);
console.log(forDemo()); // -> Logs 'Red'
        

Sometimes, it can be useful to call .bind when we don't care about the this binding, in order to create decorated functions based off of some base function:

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

//create a modified function based off kindGreeting: 
//Notice our first argument to bind is null, since we don't 
//care about the this binding in this situation.
var addOneHundred = sum.bind(null, 100);

addOneHundred(50);  // returns 150
addOneHundred(50, 65); // still returns 150

That's it for .call, .apply, and .bind!  Leave your questions in the comments below.  Thank you for reading!

Comment