A Primer On the JavaScript Apply and Call Methods

Jack Misteli

In a JavaScript world where arrow functions are everywhere, object and array destructuring are the norm, and functional programming seems to have won the popularity contest; writing about apply and call is hard.

In this world, we don’t have any practical use for these functions. So you probably don’t need to know about it today (except if a job interviewer tries to test your curiosity). But who knows? We might need those methods if another programming paradigm replaces functional programing. We’ll explore that at the end of this post.

What Are the apply and call Methods

apply and call are methods used to call functions. Done… Next section please. They really are that simple! apply and call execute the function they are attached to. You can pass two arguments to them:

  1. The this of the function you want to execute.
  2. A list of arguments.

The only difference between call and apply is that call takes a list of arguments and apply takes an array.


Learn JavaScript basics for free

Cute Tricks

Here are a few cute tricks that will help you understand call and apply.

Module: arrayToArguments.js

Math.max(1,322, 89,23, 6)
// this function returns the highest number in the list: 322

// If you have an array
const myNums = [1,322, 89,23, 6]
Math.max.apply(null, myNums)

// My nums is converted to the same list of arguments used above

Module: coolConcat.js

// To merge two arrays before ES6 you had to do something like
let firstStarWars = ["Star Wars", "The Empire Strikes Back", "Return of the Jedi"]
let prequals =  ["The Phantom Menace", "Attack of the Clones", "Revenge of the Sith"]
let allStarWars = firstStarWars.concat(prequals)

// But you can use push with apply, imagine we create the allStarWars
let allStarWars = ["Star Wars", "The Empire Strikes Back", "Return of the Jedi"]

// allStarWars in release chronology
[].push.apply(allStarWars, prequals)
// allStarWars === ["Star Wars", "The Empire Strikes Back", "Return of the Jedi", "The Phantom Menace", "Attack of the Clones", "Revenge of the Sith"]

// allStarWars in story line chronology
[].unshift.apply(allStarWars, prequals)
// allStarWars === ["The Phantom Menace", "Attack of the Clones", "Revenge of the Sith", "Star Wars", "The Empire Strikes Back", "Return of the Jedi"]

// Note that we can replace [] by any array and it will have the exact same effect
// Try changing [] for allStarWars, Array.prototype or any array!

The Cute Tricks Killer

With ES6/ES2015 and the spread operator, you can destructure objects and arrays and achieve the same results as above:

Module: dieCuteTricks.js

Math.max(...allNums)
// returns 322

allStarWars.push(...prequals)
// allStarWars === ["Star Wars", "The Empire Strikes Back", "Return of the Jedi", "The Phantom Menace", "Attack of the Clones", "Revenge of the Sith"]

If you are using ES6+, I really don’t see any reason anymore to ever use apply. You can just use .call(context, ...arguments) and you will achieve the same results. Now that apply is out of the picture we can focus on call.

call Under the Hood

If you look at the ECMAScript specification you can see that call is extremely simple. When call is called it makes a few type checks and conversions then executes the target function with the context as well as the arguments.

Module: getPlanets.js

function starWarsResource(resourceType) {
  this.information = {}
  this.resourceType =  resourceType
  this.name
}

function Planet(name) {
  this.name = name
  starWarsResource.call(this, 'planet')
}

const tatooine = new Planet('Tatooine')
// tatooine = {name: "Tatooine", information: {}, resourceType: "planet"}

// Careful look what happens if we pass a different context
function WonkyPlanet(name) {
  starWarsResource.call(null, 'planet')
}

const wonkyPlanet = new WonkyPlanet('earth')
// wonkyPlanet = {}
// But now there is are three global variables called `name`, `information` and `resourceType`

console.log(name)
// This will log 'earth' even if we never wrote `var name = 'earth'`

This is bad to say the least. We find a similar effect if we use arrow functions. First of all they cannot be constructors, so we cannot describe Planet with an arrow function. Arrow function borrow the this from their enclosing lexical context (which usually means from the environment where they are defined).

Module: getPlanets.js

const starWarsResource = (resourceType) => {
  this.information = {}
  this.resourceType = resourceType
}

function WonkyPlanet (name)  {
  // This doesn't work as expected anymore!
  this.name = name
  starWarsResource.call(this, 'planet')
}

const tatooine = new WonkyPlanet('Tatooine')
// tatooine = {}

console.log(name)
// logs "Tatooine"

starWarsResource decided of its own scope and it is the global scope.

Should I Use call?

If you are wondering if you should use it, you probably shouldn’t. There are a lot of modern patterns that allow similar effects to call.

Module: classyPlanets.js

class StarWarsResource {
  constructor(resourceType) {
    this.information = {}
    this.name
    this.resourceType = resourceType
  }
}

class Planet extends StarWarsResource  {
  constructor(name){
    super('planet')
    this.name = name
  }
}

const tatooine = new Planet('Tatooine')
// tatooine = {name: "Tatooine", information: {}, resourceType: "planet"}

This is much cleaner isn’t it? Under the hood, the babel transpiler used to use call to build classes, so it’s not completely useless. You can checkout this link to see how it works.

call and Functional Programming

I think the rise of functional programming is the main reason why call is not relevant anymore. I personally prefer a purely functional style of coding in JavaScript so it doesn’t bother me. In functional programming we try to avoid any type of object in our code. We have functions calling functions calling functions. In that perspective, functions have to be pure, states should be independent and objects immutable. In other words, functions shouldn’t care about context, they should only care about arguments.

That being said, I think it’s important not to completely disregard other design patterns. Even if I mostly use a functional style, I use different patterns every now and then.

Experimenting with Functional Calls

Now I wanted to see how I could incorporate call in a functional perspective. And here’s what I came up with:

Module: fullStars.js

// This is simply a helper which fetches data from api and returns the body
async function getAndDeserialize(url) {
  const res = await fetch(url)
  const body = await res.json()
  return body
}

// getStarWars info queries the SWAPI star wars API to get details about a Star Wars entity
// The query involves a resourceType (like I want a planet, a person or a starship...)
async function getStarWarsInfo(searchQuery) {
  console.log(this, searchQuery)

  // We extract the variable type
  const resourceType = this.resourceType
  if (!this.resourceType)
    throw new Error('resourceType is not defined!')

  // In the Star Wars API available types include: people, planets, films, starships...
  const stringQuery = `${this.resourceType}/?searchget=${searchQuery}`
  const info = await getAndDeserialize('https://swapi.co/api/' + stringQuery)

  // we set the `information` key to the result of the api call
  this.information = info.results[0]
}

async function setFilmsTitles() {
  // We declare a new key on the context called filmTitles
  this.filmTitles = []
  console.log(this.information)

  // We get the films' urls from the current context
  const filmsUrls = this.information.films

  for(let i = 0; i < filmsUrls.length; i++){
    // Getting film details from the SWAPI API
    const filmObject = await getAndDeserialize(filmsUrls[i])
    // We push the film titles after we get the details from the SWAPI API
    this.filmTitles.push(filmObject.title)
  }
}

async function getFullPerson() {
  this.resourceType = 'people'
  await getStarWarsInfo.call(this, this.name)
  await setFilmsTitles.call(this)
}

async function getFullPlanet() {
  this.resourceType = 'planets'
  await getStarWarsInfo.call(this, this.name)
  await setFilmsTitles.call(this)
}

const luke = {
  name: 'luke'
}

const tattoine = {
  name: 'Tatooine'
}

getFullPerson.call(luke)
getFullPlanet.call(tattoine)

// luke is now: {
//  filmTitles: ["The Empire Strikes Back", "Revenge of the Sith" ...]
//  information:{}eye_color: "blue"
//      films: (5) ["https://swapi.co/api/films/2/", "https://swapi.co/api/films/6/" ...]
//      gender: "male"
//      hair_color: "blond"
//      height: "172"
//      homeworld: "https://swapi.co/api/planets/1/"
//      mass: "77"
//    }
//    name: "luke"
//    resourceType: "people"
//  }

// tattoine is now {
//    filmTitles: (2) ["Revenge of the Sith", "A New Hope"]
//    information: {name: "Alderaan", rotation_period: "24", orbital_period: "364"...}
//    name: "Tatooine"
//    resourceType: "planets"}

This code looks functional because we have functions calling functions. But it isn’t. It’s important to remember that in JavaScript a function is an object, a very special object. In fullStars.js, we use functions like objects which each have a state so I don’t think it’s functional.

call requires us to think statefully so it can’t be used in a purely functional approach. That being said, JavaScript by nature is made of objects as is made clear by the very existence of this in every function. Even if your code does not strictly respect functional rules you can still consider it functional. In law, we call this opposition the letter and the spirit of the law.

May the force be with you.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#