This article was originally published at: blog.bitsrc.io/understanding-call-bind-and-..
If you are learning JavaScript, you might have seen the this
keyword. The this
keyword in JavaScript behaves differently compared to other programming languages. This causes a lot of confusion for programmers.
In other object-oriented programming languages, the this
keyword always refers to the current instance of the class. Whereas in JavaScript, the value of this
depends on how a function is called.
Let’s look at some examples to demonstrate the behavior of this
in JavaScript.
Example 1:
const person = {
firstName: 'John',
lastName: 'Doe',
printName: function() {
console.log(this.firstName + ' ' + this.lastName);
}
};
Now let’s execute the printName
method.
person.printName();
This prints:
John Doe
Here, I am calling the printName()
method using the person
object, so the this
keyword inside the method refers to the person
object.
Let’s write the below snippet at the end of the above code.
const printFullName = person.printName;
printFullName();
What do you think the console.log will now output? Surprisingly, this prints:
undefined undefined
Why does this happen?
Here, we are storing a reference of person.printName
to printFullName
variable. After that, we are calling it without an object reference, so this
will now refer to the window (global) object or undefined (in strict mode).
If the script is in strict mode, this
refers to undefined
, so console.log()
will return an error.
Example 2:
const counter = {
count: 0,
incrementCounter: function() {
console.log(this);
this.count++;
}
}
document.querySelector('.btn').addEventListener('click', counter.incrementCounter);
What would be the value of this
inside incrementCounter()
method?
In the above snippet, the this
keyword refers to the DOM element where the event happened, not the counter
object.
So we can see that the this
keyword inside a function refers to different objects depending on how the function is called and sometimes we accidentally lose reference to the this
variable. So how can we prevent that from happening?
Call( ), Bind( ), and Apply( ) to Rescue
We use Call, Bind and Apply methods to set the this
keyword independent of how the function is called. This is especially useful for the callbacks (as in the above example).
We know that functions are a special kind of objects in JavaScript. So they have access to the same methods and properties as objects. To prove functions are objects, we can do something like this, for example:
function greeting() {
console.log('Hello World');
}
greeting.lang = 'English';
// Prints 'English'
console.log(greeting.lang);
JavaScript also provides some special methods and properties to every function object. So every function in JavaScript inherits those methods. Call, bind, and apply are some of the methods that every function inherits.
Bind( )
The bind method creates a new function and sets the this
keyword to the specified object.
Syntax:
`function`.bind(thisArg, optionalArguments)
For example:
Let’s suppose we have two person objects.
const john = {
name: 'John',
age: 24,
};
const jane = {
name: 'Jane',
age: 22,
};
Let’s add a greeting function:
function greeting() {
console.log(`Hi, I am ${this.name} and I am ${this.age} years old`);
}
We can use the bind
method on the greeting
function to bind the this
keyword to john
and jane
objects. For example:
const greetingJohn = greeting.bind(john);
// Hi, I am John and I am 24 years old
greetingJohn();
const greetingJane = greeting.bind(jane);
// Hi, I am Jane and I am 22 years old
greetingJane();
Here greeting.bind(john)
creates a new function with this
set to john
object, which we then assign to greetingJohn
variable.
We can also use bind
in case of callbacks and event handlers. For example:
const counter = {
count: 0,
incrementCounter: function() {
console.log(this);
this.count++;
}
};
document.querySelector('.btn').addEventListener('click', counter.incrementCounter.bind(counter));
In the above example, the this
keyword inside the incrementCounter
method will now correctly refer to the counter
object instead of the event object.
Bind() can also accept arguments
We can also pass extra arguments to the bind method. The general syntax for this is function.bind(this, arg1, arg2, ...)
. For example:
function greeting(lang) {
console.log(`${lang}: I am ${this.name}`);
}
const john = {
name: 'John'
};
const jane = {
name: 'Jane'
};
const greetingJohn = greeting.bind(john, 'en');
greetingJohn();
const greetingJane = greeting.bind(jane, 'es');
greetingJane();
In the above example, the bind
method creates a new function with certain parameters predefined (lang
in this case) and this
keyword set to the john
and jane
objects.
Call ( )
The call method sets the this
inside the function and immediately executes that function.
The difference between call()
and bind()
is that the call()
sets the this
keyword and executes the function immediately and it does not create a new copy of the function, while the bind()
creates a copy of that function and sets the this
keyword.
Syntax:
`function`.call(thisArg, arg1, agr2, ...)
For example:
function greeting() {
console.log(`Hi, I am ${this.name} and I am ${this.age} years old`);
}
const john = {
name: 'John',
age: 24,
};
const jane = {
name: 'Jane',
age: 22,
};
// Hi, I am John and I am 24 years old
greeting.call(john);
// Hi, I am Jane and I am 22 years old
greeting.call(jane);
The above example is similar to the bind()
example except that call()
does not create a new function. We are directly setting the this
keyword using call()
.
Call () can also accept arguments
Call()
also accepts a comma-separated list of arguments. The general syntax for this is function.call(this, arg1, arg2, ...)
For example:
function greet(greeting) {
console.log(`${greeting}, I am ${this.name} and I am ${this.age} years old`);
}
const john = {
name: 'John',
age: 24,
};
const jane = {
name: 'Jane',
age: 22,
};
// Hi, I am John and I am 24 years old
greet.call(john, 'Hi');
// Hello, I am Jane and I am 22 years old
greet.call(jane, 'Hello');
Apply ( )
The apply()
method is similar to call()
. The difference is that the apply()
method accepts an array of arguments instead of comma-separated values.
Syntax:
`function`.apply(thisArg, [argumentsArr])
For example:
function greet(greeting, lang) {
console.log(lang);
console.log(`${greeting}, I am ${this.name} and I am ${this.age} years old`);
}
const john = {
name: 'John',
age: 24,
};
const jane = {
name: 'Jane',
age: 22,
};
// en
// Hi, I am John and I am 24 years old
greet.apply(john, ['Hi', 'en']);
// es
// Hola, I am Jane and I am 22 years old
greet.apply(jane, ['Hola', 'es']);
Conclusion
We have learned how this
keyword behaves differently in JavaScript than in other object-oriented languages. The call, bind and apply methods can be used to set the this
keyword independent of how a function is called.
The bind method creates a copy of the function and sets the this
keyword, while the call and apply methods set the this
keyword and calls the function immediately.
That’s it and if you found this article helpful, please click the like 🙂 button, you can also follow me on Hashnode, Medium and Twitter, and if you have any doubt, feel free to comment! I’d be happy to help :)