Call, Bind and Apply

tl;dr call, bind and apply are 3 really important methods that are available on all Javascript functions OOTB. Each method does the same kind of thing, but the invocations are different.

Call

call attaches this into a function, and then executes it immediately:

var pokemon1 = {
  name: "Gary",
  species: "Charizard",
  type1: "Fire",
  type2: "Flying",
  health: 1000,
  atk: 94,
};

var pokemon2 = {
  name: "Barry",
  species: "Blastoise",
  type1: "Water",
  type2: "",
  health: 1000,
  atk: 91,
};

function getFullName(connectingWord) {
  var fullName = this.name + connectingWord + this.species;
  return fullName;
}

getFullName.call(pokemon1, " is a "); // Gary is a Charizard getFullName.call(pokemon2, ' the ') // Barry the Blastoise

Apply

apply does the same thing as call, except it takes in an array of arguments:

var pokemon1 = {
    name: 'Gary',
    species: 'Charizard',
    type1: 'Fire',
    type2: 'Flying',
    health: 1000,
    atk: 94
};


var pokemon2 = {
    name: 'Barry',
    species: 'Blastoise',
    type1: 'Water',
    type2: '',
    health: 1000,
    atk: 91
};

function getAttacks(attack1, attack2) {
  console.log(this.name + " has the following attacks: ")
  console.log(attack1)
  console.log(attack2)

}

getAttacks.apply(pokemon1, ['Fire Spin', 'Blast Burn']) getAttacks.apply(pokemon2, ['Water Gun', 'Hydro Cannon'])

Bind

To understand bind a little better, let’s go deeper using the above example:

var pokemon1 = {
  name: "Gary",
  species: "Charizard",
  type1: "Fire",
  type2: "Flying",
  health: 1000,
  atk: 94,
  attack: function () {
    return (
      Math.floor(Math.random() * (this.atk + 10 - this.atk + 1)) + this.atk
    );
  },
  getFullName: function () {
    var fullName = this.name + " the " + this.species;
    return fullName;
  },
};

var pokemon2 = {
  name: "Barry",
  species: "Blastoise",
  type1: "Water",
  type2: "",
  health: 1000,
  atk: 91,
  attack: function () {
    return (
      Math.floor(Math.random() * (this.atk + 10 - this.atk + 1)) + this.atk
    );
  },
  getFullName: function () {
    var fullName = this.name + " the " + this.species;
    return fullName;
  },
};

function startBattle(pokemon1, pokemon2) {
  console.log(pokemon1.getFullName() + " vs " + pokemon2.getFullName());
}

startBattle(pokemon1, pokemon2); // "Gary the Charizard vs Barry the Blastoise"

In the above example we can see there are some repeated methods as part of each function. It would be a lot clearer if we could define a function we can reuse on each Pokemon:

var pokemon1 = {
  name: "Gary",
  species: "Charizard",
  type1: "Fire",
  type2: "Flying",
  health: 1000,
  atk: 94,
  attack: function () {
    return (
      Math.floor(Math.random() * (this.atk + 10 - this.atk + 1)) + this.atk
    );
  },
};

var pokemon2 = {
  name: "Barry",
  species: "Blastoise",
  type1: "Water",
  type2: "",
  health: 1000,
  atk: 91,
  attack: function () {
    return (
      Math.floor(Math.random() * (this.atk + 10 - this.atk + 1)) + this.atk
    );
  },
};

function getFullName() {
  var fullName = this.name + " the " + this.species;
  return fullName;
}

function startBattle(pokemon1, pokemon2) {
  console.log(
    getFullName.bind(pokemon1)() + " vs " + getFullName.bind(pokemon2)()
  ); // "Gary the Charizard vs Barry the Blastoise"
}

startBattle(pokemon1, pokemon2);

Here you can see we’ve refactored the function to be standalone, and we are now invoking it using bind. This causes a copy of the pokemon object be made.

Taking this example a step further, let’s see if we can refactor the attack function a little to demonstrate some other features of the bind function:

var pokemon1 = {
    name: 'Gary',
    species: 'Charizard',
    type1: 'Fire',
    type2: 'Flying',
    health: 1000,
    atk: 94
};


var pokemon2 = {
    name: 'Barry',
    species: 'Blastoise',
    type1: 'Water',
    type2: '',
    health: 1000,
    atk: 91
};

function getFullName() {
  var fullName = this.name + ' the ' + this.species;
  return fullName;
}

function attack(opponent) {
  const name1 = getFullName.bind(this)()
  const name2 = getFullName.bind(opponent)()

  const hit = Math.floor(Math.random() * ((this.atk + 10) - this.atk + 1)) + this.atk // Calulates attack damage using this
  opponent.health -= hit // Removes points from opponent

  console.log(name1 + " hits for " + hit + " points")
  console.log(name2 + "'s' health is " + opponent.health)
}

function startBattle(pokemon1, pokemon2) {
  console.log(getFullName.bind(pokemon1)() + ' vs ' + getFullName.bind(pokemon2)())
}

startBattle(pokemon1, pokemon2)

const pokemonOneAttack = attack.bind(pokemon1)
const pokemonTwoAttack = attack.bind(pokemon2)

pokemonOneAttack(pokemon2)
pokemonTwoAttack(pokemon1)

pokemonOneAttack(pokemon2)
pokemonTwoAttack(pokemon1)

console.log(pokemon1) console.log(pokemon2)

Using bind we’ve now managed to create a standalone attack function that will calculate the hit damage of the current pokemon, and also take that amount off of the opponent. We can now reuse the pokemonOneAttack and pokemonTwoAttack functions whenever applicable.