Skip to main content

Command Palette

Search for a command to run...

The Magic of this, call(), apply(), and bind() in JavaScript

Updated
•7 min read•View as Markdown
The Magic of this, call(), apply(), and bind() in JavaScript

Hola readers, this blog would be making your JS foundation stronger and understanding the fine details of JS, we would be discussing call(), apply(), bind() and this with some examples.

What's the point in this ?

In JS, this simply refers to the object that is currently executing the function. this depends on the environment where JS is present. In a browser this is the windows object.

this in node JS environment is different depending on the scope where it is used.

this is a reference to the object that calls the function.

const person = {
  name: "Saurav",
  greet() {
    console.log(this.name);
  }
};

person.greet();    //Saurav

this inside Objects

In the above code block the out put is Saurav because greet is called as a method and normal function get this when they are called.

Now you might wonder why do we even need to call this.name, can't we simply console name as it is within the object itself?

const person = {
  name: "Saurav",
  greet() {
    console.log(name);
  }
};

person.greet();    //undefined

The problem with the above code block is that objects do not create a scope.

Scopes only comes from:

  • functions

  • blocks (let / const)

  • modules

In such cases it is this that tells JS to use the object that called this function.

so when we run person.greet() internally JS does this = person and when we do this.name, it is similar to saying person.name and it prints the name value inside the object.

Real Reason behind using this

this makes functions generic and reusable, by making this = whoever called the function. If objects created scope, this would break many patterns like:

  • method borrowing

  • prototypes

  • classes

  • call/apply/bind

  • functional reuse

So JavaScript instead used dynamic binding with this.

this inside normal functions

In my previous blog, I have discussed how this worked with arrow functions, in case you are interested then you should visit this

In a regular function, the value of this depends on how the function is called, not where it is written. this is decided at call time.

const person = {
  name: "Saurav",
  greet() {
    console.log(this.name);
  }
};

person.greet();    //Saurav

In the above code block greet is used as a method, and called using dot (.) notation, the object before dot (.) becomes the this for that function. So in above case

this = person

Now suppose you do something like :

const person = {
  name: "Saurav",
  greet() {
    console.log(this.name);
  }
};

const greetMe = person.greet;   
greetMe()    //undefined

we know that for a regular function this is decided at call time. So we are storing the reference of the greet() which is inside of person object inside the greetMe variable. And later on we do greetMe() . Now here during call time, greetMe is called as a normal function and is not associated with any object, so the value of this inside the greet() becomes as "", and when we try to access the name property then we get undefined as the answer.

call() me whenever you need me.

You went through this earlier on, you understood that how this was set for functions during call time. But consider a scenario, you want to set the value of this by yourself, in such a case, you gotta call call() .

functionName.call(thisArg, arg1, arg2)

Whatever object your want to associate with your function, you keep it inside the thisArg, and arg1, arg2, .... argN are the arguments passed to that function.

function greet() {
  console.log("Hello " + this.name);
}

const user = { name: "Saurav" };

greet.call(user);

In the above code block, you are calling the function greet and using call to the set the value of this for it. Inside call you pass user object which means that inside the greet function now this = user, so when you do this.name, it becomes user.name.

Polyfill for call() (BONUS)

Function.prototype.myCall = function (context, ...args) {
  // Step 1: Handle null/undefined
  context = context || globalThis;

  // Step 2: Create unique key to avoid override
  const fnSymbol = Symbol();

  // Step 3: Attach function to object
  context[fnSymbol] = this;

  // Step 4: Call function
  const result = context[fnSymbol](...args);

  // Step 5: Clean up
  delete context[fnSymbol];

  return result;
};

apply when-ever you want

call and apply almost do the same things, the only notable difference is that apply will pass arguments in the form of an array.

functionName.apply(thisArg, [arg1, arg2, arg3]);

In this you are passing thisArg which would be the this part and instead of passing arg1, arg2,...argN individually, we send it in an array.

function greet(city, country) {
  console.log(this.name + " from " + city + ", " + country);
}

const person = { name: "Saurav" };

greet.apply(person, ["BBSR", "India"]);

//Output
//Saurav from BBSR, India

Inside the apply, what would be happening is that it is setting person.greet = greet and then calling person.greet("BBSR", "India");

Personally I feel apply could come handy when we already have arguments in an array.

function sum(a, b, c) {
  return a + b + c;
}

const arr = [1, 2, 3];

sum.apply(null, arr); // 6

In the above code block, you can see that sum is a function that accepts three parameters, and we have an array of numbers, so instead of individually passing the numbers from the array as arguments, we simply use apply.

Modern alternative to apply

apply is old school, you will see modern codebases using it less and often do the above code block using the spread operator.

function sum(a, b, c) {
  return a + b + c;
}

const arr = [1, 2, 3];

sum(...arr); // 6

Polyfill for apply()

Function.prototype.myApply = function (context, args) {
  // Step 1: Handle null/undefined
  context = context || globalThis;

  // Step 2: Create unique key
  const fnSymbol = Symbol();

  // Step 3: Attach function
  context[fnSymbol] = this;

  // Step 4: Call with array
  const result = args
    ? context[fnSymbol](...args)
    : context[fnSymbol]();

  // Step 5: Clean up
  delete context[fnSymbol];

  return result;
};

Bound to use bind() ?

bind() does NOT call the function immediately. It returns a new function with this permanently set.

const newFn = originalFn.bind(thisArg, arg1, arg2);

In the above syntax for bind() thisArg is this, arg1, arg2 are optional preset arguments. The thing which makes bind() different from it's counterparts call() and apply() is that call() does not run immediately, it returns a function which you call use it later on.

const person = {
  name: "Saurav",
  greet() {
    console.log(this.name);
  }
};

setTimeout(person.greet, 1000); //undefined
setTimeout(person.greet.bind(person), 1000); //Saurav
function greet(city) {
  console.log(this.name + " from " + city);
}

const person = { name: "Saurav" };

const bound = greet.myBind(person, "BBSR");

bound(); // Saurav from BBSR

Real bind also works with new :

function Person(name) {
  this.name = name;
}

const BoundPerson = Person.bind(null);

const p = new BoundPerson("Saurav");

console.log(p.name); // Saurav

Difference Table

Feature call() apply() bind()
Executes immediately? ✅ Yes ✅ Yes ❌ No
Returns a function? ❌ No ❌ No ✅ Yes
How arguments are passed? Individually As an array Individually (preset)
Purpose Call function with custom this Same as call but with array args Create new function with fixed this
Use case Direct invocation When args are in array Reuse later / callbacks
Syntax fn.call(obj, a, b) fn.apply(obj, [a, b]) fn.bind(obj, a, b)
Partial application? ❌ No ❌ No ✅ Yes
Used in async (setTimeout etc)? ❌ No ❌ No ✅ Yes
Changes original function? ❌ No ❌ No ❌ No

call → call now

apply → call now (array)

bind → call later