Explain `Function.prototype.bind` in JavaScript
TL;DR
Function.prototype.bind
is a method in JavaScript that allows you to create a new function with a specific this
value and optional initial arguments. It's primary purpose is to:
- Binding
this
value to preserve context: The primary purpose ofbind
is to bind thethis
value of a function to a specific object. When you callfunc.bind(thisArg)
, it creates a new function with the same body asfunc
, but withthis
permanently bound tothisArg
. - Partial application of arguments:
bind
also allows you to pre-specify arguments for the new function. Any arguments passed tobind
afterthisArg
will be prepended to the arguments list when the new function is called. - Method borrowing:
bind
allows you to borrow methods from one object and apply them to another object, even if they were not originally designed to work with that object.
The bind
method is particularly useful in scenarios where you need to ensure that a function is called with a specific this
context, such as in event handlers, callbacks, or method borrowing.
Function.prototype.bind
Function.prototype.bind
allows you to create a new function with a specific this
context and, optionally, preset arguments. bind()
is most useful for preserving the value of this
in methods of classes that you want to pass into other functions.
bind
was frequently used on legacy React class component methods which were not defined using arrow functions.
const john = {age: 42,getAge: function () {return this.age;},};console.log(john.getAge()); // 42const unboundGetAge = john.getAge;console.log(unboundGetAge()); // undefinedconst boundGetAge = john.getAge.bind(john);console.log(boundGetAge()); // 42const mary = { age: 21 };const boundGetAgeMary = john.getAge.bind(mary);console.log(boundGetAgeMary()); // 21
In the example above, when the getAge
method is called without a calling object (as unboundGetAge
), the value is undefined
because the value of this
within getAge()
becomes the global object. boundGetAge()
has its this
bound to john
, hence it is able to obtain the age
of john
.
We can even use getAge
on another object which is not john
! boundGetAgeMary
returns the age
of mary
.
Use cases
Here are some common scenarios where bind
is frequently used:
Preserving context and fixing the this
value in callbacks
When you pass a function as a callback, the this
value inside the function can be unpredictable because it is determined by the execution context. Using bind()
helps ensure that the correct this
value is maintained.
class Person {constructor(name) {this.name = name;}greet() {console.log(`Hello, my name is ${this.name}`);},};const john = new Person('John Doe');// Without bind(), `this` inside the callback will be the global objectsetTimeout(john.greet, 1000); // Output: "Hello, my name is undefined"// Using bind() to fix the `this` valuesetTimeout(john.greet.bind(john), 2000); // Output: "Hello, my name is John Doe"
You can also use arrow functions to define class methods for this purpose instead of using bind
. Arrow functions have the this
value bound to its lexical context.
class Person {constructor(name) {this.name = name;}greet = () => {console.log(`Hello, my name is ${this.name}`);};}const john = new Person('John Doe');setTimeout(john.greet, 1000); // Output: "Hello, my name is John Doe"
Partial application of functions (currying)
bind
can be used to create a new function with some arguments pre-set. This is known as partial application or currying.
function multiply(a, b) {return a * b;}// Using bind() to create a new function with some arguments pre-setconst multiplyBy5 = multiply.bind(null, 5);console.log(multiplyBy5(3)); // Output: 15
Method borrowing
bind
allows you to borrow methods from one object and apply them to another object, even if they were not originally designed to work with that object. This can be handy when you need to reuse functionality across different objects
const person = {name: 'John',greet: function () {console.log(`Hello, ${this.name}!`);},};const greetPerson = person.greet.bind({ name: 'Alice' });greetPerson(); // Output: Hello, Alice!
Practice
Try implementing your own Function.prototype.bind()
method on GreatFrontEnd.