Every developer should fully understand how they work and be able to discern the subtle differences between them.
So you know, JS functions are first-class citizens.
Which means: they’re all just object values — all instances of the Function
class, with methods and properties.
So bind()
, apply()
, and call()
are 3 essential methods every JavaScript function has.
Were you with me in the painful early days of React; when we still did this? 👇
This was just one of the multiple applications of bind()
— a seriously underrated JavaScript method.
// damn class components are such a chore to write now
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
greet() {
alert(`Hi, I'm ${this.props.name}!`);
}
// remember render()?
render() {
return (
<button onClick={this.greet.bind(this)}>Click me</button>
);
}
}
export default MyComponent;
sayName()
would be a mess without bind()
— the alert()
would never work.
Because internally React is doing something fishy with this method that completely screws up what this
means inside it.
Before sayName
would have had absolutely no problems showing the alert — just like in this other class:
class Person {
props = { name: 'Tari' };
greet() {
console.log(`Hi, I'm ${this.props.name}!`);
}
}
const person = new Person();
person.greet();
But guess what React does to the greet
event handler method behind the scenes?
It reassigns it to another variable:
class Person {
props = { name: 'Tari' };
greet() {
console.log(`Hi, I'm ${this.props.name}!`);
}
}
// reassign to another variable:
const greet = Person.prototype.greet;
// ❌ bad idea
greet();
So guess what happens to this
— it’s nowhere to be found:
This is where bind
comes to the rescue — it changes this
to any instance object you choose:
So we’ve binded the function to the object — the bind target.
(I know it’s “bound” but let’s say binded just like how we say “indexes” for “index” instead of “indices”).
It’s immutable — it returns the binded function without changing anything about the original one.
And this lets us use it as many times as possible:
vs call()
There’s only a tiny difference between call
and bind
bind
creates the binded function for you to use as many times as you like.
But call
? It creates a temporary binded function on the fly and calls it immediately:
class Person {
constructor(props) {
this.props = props;
}
greet() {
console.log(`Hi, I'm ${this.props.name}`);
}
}
const person = new Person({ name: 'Tari' });
const greet = Person.prototype.greet;
greet.bind(person)();
greet.call(person);
So call()
is basically bind()
+ a call.
But what about when the function has arguments? What do we do then?
No problem at all — just pass them as more arguments to call
:
class Person {
constructor(props) {
this.props = props;
}
greet(name, favColor) {
console.log(
`Hi ${name}, I'm ${this.props.name} and I love ${favColor}`
);
}
}
const person = new Person({ name: 'Tari' });
const greet = Person.prototype.greet;
// bind(): normal argument passing to binded function
greet.bind(person)('Mike', 'blue🔵');
// call(): pass as more arguments
greet.call(person, 'Mike', 'blue🔵');
And you can actually do the same with bind()
:
// the same thing
greet.bind(person)('Mike', 'blue🔵');
greet.bind(person, 'Mike', 'blue🔵')();
vs apply()
At first you may think apply()
does the exact same thing as call()
:
class Person {
constructor(props) {
this.props = props;
}
greet() {
console.log(`Hi, I'm ${this.props.name}`);
}
}
const person = new Person({ name: 'Tari' });
const greet = Person.prototype.greet;
greet.call(person); // Hi, I'm Tari
greet.apply(person); // Hi, I'm Tari
But just like bind()
vs call()
there’s a subtle difference to be aware of:
Arguments passing:
class Person {
constructor(props) {
this.props = props;
}
greet(name, favColor) {
console.log(
`Hi ${name}, I'm ${this.props.name} and I love ${favColor}`
);
}
}
const person = new Person({ name: 'Tari' });
const greet = Person.prototype.greet;
//💡call() -- pass arguments with comma separated
greet.call(person, 'Mike', 'blue🔵'); // Hi, I'm Tari
//💡apply() -- pass arguments with array
greet.apply(person, ['Mike', 'blue🔵']); // Hi, I'm Tari
One mnemonic trick I use to remember the difference:
- call() is for commas
- apply() is for arrays
Recap
bind()
— bind to this and return a new function, reusablecall()
— bind + call function, pass arguments with commasapply()
— bind + call function, pass arguments with array
11 Amazing New JavaScript Features in ES13
This guide will bring you up to speed with all the latest features added in ECMAScript 13. These powerful new features will modernize your JavaScript with shorter and more expressive code.