Back in February, I created a quiz to help myself and others better understand the this
keyword in JavaScript. At the time, I didn't delve into ways to explicitly determine what object this
is referring to, i.e. using call
, apply
, and bind
methods.
Fast forward to this week, I came across the call
method when exploring the zooming effect of d3.js (a data visualization library):
svg.call(d3.zoom().on('zoom', () => {
g.attr('transform', d3.event.transform)
}))
As with any tools, it is easy to forget how to use them if you don't encounter them frequently. So here's a quick reference to all three methods as a reminder of their usage and differences.
Call() and Apply()
How They are Similar
Both call
and apply
methods allow us to specify what the this
keyword should reference, and invoke the function immediately.
For instance:
function covidAlert() {
alert(`To stop the spread, please ${this.outdoor}.`)
}
const measures = {
indoor: 'wash your hands',
outdoor: 'wear a mask',
social: 'keep 6-feet distance'
}
We cannot use measures.covidAlert()
because measures
object doesn't have covideAlert
function as its execution context.
This is where the call
or apply
method comes to our rescue:
covidAlert.call(measures)
// or:
covidAlert.apply(measures)
// => To stop the spread, please wear a mask.
How They are Different
If you want to pass arguments to the function, you can do so by passing in the arguments one by one using the call
method.
function covidAlert(phaseNum, date, state) {
alert(`To stop the spread, please ${this.outdoor}, so we can enter phase ${phaseNum} on ${date}. We are #${state}Smart.`)
}
const measures = {
indoor: 'wash your hands',
outdoor: 'wear a mask',
social: 'keep 6-feet distance'
}
covidAlert.call(measures, 'four', 'July 20th', 'NewYork')
// => To stop the spread, please wear a mask, so we can enter phase four on July 20th. We are #NewYorkSmart.
With apply
, you should pass in an array of arguments, like so:
function covidAlert(phaseNum, date, state) {
alert(`To stop the spread, please ${this.outdoor}, so we can enter phase ${phaseNum} on ${date}. We are #${state}Smart.`)
}
const measures = {
indoor: 'wash your hands',
outdoor: 'wear a mask',
social: 'keep 6-feet distance'
}
const reopenDetails = ['four', 'July 20th', 'NewYork']
covidAlert.apply(measures, reopenDetails)
// => To stop the spread, please wear a mask, so we can enter phase four on July 20th. We are #NewYorkSmart.
Bind()
The bind
method is useful when you do NOT want to invoke the function immediately. Instead, bind
creates a copy of the function that you can invoke later.
You can also pass in arguments with the bind
method one by one, just like how you do it with call
:
function covidAlert(phaseNum, date, state) {
alert(`To stop the spread, please ${this.outdoor}, so we can enter phase ${phaseNum} on ${date}. We are #${state}Smart.`)
}
const measures = {
indoor: 'wash your hands',
outdoor: 'wear a mask',
social: 'keep 6-feet distance'
}
const functionToBind = covidAlert.bind(measures, 'four', 'July 20th', 'NewYork')
functionToBind()
// => To stop the spread, please wear a mask, so we can enter phase four on July 20th. We are #NewYorkSmart.
And that's a wrap! To learn more about the specifics of this
keyword and its execution context in JavaScript, I recommend reading the following two articles. Their clear explanations and practical use cases really solidified my understanding of function binding:
- Understanding This, Bind, Call, and Apply in JavaScript by Tania Rascia
- Understanding Call, Bind and Apply Methods in JavaScript by Sukhjinder Arora
Top comments (2)
Great explanation Annie.
As functional-programming-centric developer myself, I don't use these types of constructs.
I use a subset of the language to achieve all the goals that need to get accomplished, as performantly, robust, readable, and changeable as possible.
For example, ideally, I don't use...
bind
call
"this" keyword
"function" keyword
"class" keyword
"extends" keyword (inheritance)
interface classes
abstract classes
getters and setters
overloading
overriding
virtual methods
optional parameters
asynchronous callbacks
let (unless it's a value type that I will explicitly reassign)
implicit casts
implicit type checks
implicit null/undefined checks
single quotes
"require" keyword
polymorphic functions
recursion
Oh wow, thanks for listing all the constructs (helpers?) that can potentially create more headaches down the road. Indeed, I do recall running into issues with async callbacks. I also prefer using functional components in React.
While I'm still not experienced enough to choose between object-oriented and functional programming, your comment motivates me to reach that level soon :)