JavaScript offers call(), apply(), and bind() to control the value of this inside functions. These methods are useful for managing function context, especially in object-oriented scenarios.
- call(), apply(), and bind() change the this context of a function.
- Commonly used when sharing functions between objects.
- Polyfill provide modern features in older environments by recreating missing functionality.
1. Bind() Method
The bind() method returns a new function with a permanently set this value.
- The first argument defines the "this" context.
- Additional arguments are stored and used when the new function runs.
- The original function is not modified.
Syntax:
bind(thisArg)
bind(thisArg, arg1, arg2, /* …, */ argN)
[Example 1]: Let us first see what will be the actual output with the bind method that is given by javascript:
let nameObj = {
name: "Tony"
}
let PrintName = {
name: "steve",
sayHi: function () {
// Here "this" points to nameObj
console.log(this.name);
}
}
let HiFun = PrintName.sayHi.bind(nameObj);
HiFun();
Binding nameObj sets this to nameObj, allowing access to its value.
[Example 2]: We will implement our bind polyfill using a prototype on the Object class in the above example:
let nameObj = {
name: "Tony"
}
let PrintName = {
name: "steve",
sayHi: function () {
console.log(this.name);
}
}
Object.prototype.MyBind = function (bindObj) {
// Here "this" will be sayHi function
bindObj.myMethod = this;
return function () {
bindObj.myMethod();
}
}
let HiFun = PrintName.sayHi.MyBind(nameObj);
HiFun();
The polyfill behaves the same as the native bind() method. We attach MyBind to the object prototype, and it accepts the object (here, nameObj) we want to bind to PrintName.sayHi.
Now, let us only focus only on this part of the code (Object.prototype.MyBind):
Object.prototype.MyBind = function (bindObj) {
// Here "this" will be sayHi function
bindObj.myMethod = this;
return function () {
bindObj.myMethod();
}
}
We pass nameObj into MyBind, then assign myMethod to store the function reference.
[Example 3]: To understand this step better, we’ll view the structure of bindObj after adding myMethod to it.
let nameObj={
name:"Tony"
}
let PrintName={
name:"steve",
sayHi:function(){
console.log(this.name);
}
}
Object.prototype.MyBind=function(bindObj) {
// Here "this" will be sayHi function
bindObj.myMethod=this;
console.log("bindObj ->",bindObj);
return function(){
bindObj.myMethod();
}
}
let HiFun=PrintName.sayHi.MyBind(nameObj);
We don’t call HiFun() here because the goal is only to inspect how bindObj changes. After running bindObj.myMethod = this, the object now holds a reference to the sayHi function:
let bindObj = {
name: "Tony",
myMethod: function sayHi() {
// Here 'this' will be bindObj
console.log(this.name);
}
}
We want sayHi to run with this set to nameObj. To achieve that, the polyfill stores the original function (sayHi) inside nameObj as myMethod. Then, when myMethod() is called, this naturally becomes nameObj, allowing access to its data (name: "Tony"). This mimics how the native bind() method works.
[Example 4]: This bind polyfill example shows how parameters can be passed to MyBind:
let nameObj = {
name: "Tony"
}
let PrintName = {
name: "steve",
sayHi: function (age) {
console.log(this.name + " age is " + age);
}
}
Object.prototype.MyBind = function (bindObj, ...args) {
bindObj.myMethod = this;
return function () {
bindObj.myMethod(...args);
}
}
let HiFun = PrintName.sayHi.MyBind(nameObj, 42);
HiFun();
The ...args rest parameter collects all additional arguments into an array. In this example, ...args becomes [42], which is passed to the function when the bound function is executed.
2. Call() Method
The call() method invokes the function immediately and sets this to the first argument. Any additional arguments are passed directly to the function.
Syntax:
call(objectInstance)
call(objectInstance, arg1, /* …, */ argN)
[Example 1]: Before implementing our own call polyfill let us see the call method which is given by javascript
let nameObj = {
name: "Tony"
}
let PrintName = {
name: "steve",
sayHi: function (age) {
console.log(this.name + " age is " + age);
}
}
PrintName.sayHi.call(nameObj, 42);
NOTE: The call() method invokes the function immediately and does not return a new function.
[Example 2]: Write our own call polyfill:
let nameObj = {
name: "Tony"
}
let PrintName = {
name: "steve",
sayHi: function (age) {
console.log(this.name + " age is " + age);
}
}
Object.prototype.MyCall = function (bindObj, ...args) {
bindObj.myMethod = this;
bindObj.myMethod(...args);
}
PrintName.sayHi.MyCall(nameObj, 42);
This works like the native call() method. The only difference from the bind polyfill is that MyCall does not return a new function - just like JavaScript’s built-in call().
3. Apply() Method
The apply() method calls a function immediately and sets this to the first argument passed. Unlike call(), the additional arguments must be provided as an array, which are then passed to the function.
Syntax:
apply(objectInstance)
apply(objectInstance, argsArray)
[Example 1]: For the final time let us see apply method given by javascript:
let nameObj = {
name: "Tony"
}
let PrintName = {
name: "steve",
sayHi: function (...age) {
console.log(this.name + " age is " + age);
}
}
PrintName.sayHi.apply(nameObj, [42]);
With apply(), we pass arguments as an array. This is the main difference between call() and apply() - call() accepts individual arguments, while apply() accepts them in array form.
[Example 2]: Now let us write our final polyfill which is apply polyfill:
let nameObj = {
name: "Tony"
}
let PrintName = {
name: "steve",
sayHi: function (age) {
console.log(this.name + " age is " + age);
}
}
Object.prototype.MyApply = function (bindObj, args) {
bindObj.myMethod = this;
bindObj.myMethod(...args);
}
PrintName.sayHi.MyApply(nameObj, [42]);
In this polyfill, we pass arguments as an arrayjust like the native apply() method. Other than accepting arguments differently, the implementation works the same way as our call() polyfill.