Bind, Call and Apply

Beginner JavaScript

Enjoy these notes? Want to Slam Dunk JavaScript?

These are notes based on my Beginner JavaScript Video Course. It's a fun, exercise heavy approach to learning Modern JavaScript from scratch.

Use the code BEGINNERJS for an extra $10 off.

BeginnerJavaScript.com

This lesson will focus on the bind, call and apply functions, which are all used to change the scope of what this is equal to inside of a function or a method.

Wes does not use these very often, but they are common interview questions so we will go over them so you have an understanding of how it works.

Go into the playground directory and add a file bind-call-apply.html. Add our HTML base and a script tag wtihin the body tag.

We will start with a simple example of an object that has a method on it.

Create a person object on it with property of name and sayHi. The name can be your name, and sayHi will equal a function that logs "Hey!" with the person's name.

const person = {
  name: 'Wes Bos',
  sayHi: function() {
    return `hey ${this.name}`;
  }
};

Let's change it shorthand, which is the exact same thing as above, it's just shorter syntax for the sayHi property.

const person = {
  name: 'Wes Bos',
  sayHi() {
    return `hey ${this.name}`;
  }
};

Open that in the browser and take a look at it in the console.

calling sayHi function on person object in console

As you can see, it says "hey wes bos".

Why is that?

That is because sayHi is a method, and when a method is called, the way they get the this value is they look to the left of the dot to see what they are bound against.

The sayHi method will give us the object it was run against. This is no different than if we were to have class or prototype. The this is going to be equal to whatever was to the left of the dot.

Now , what if you were to take the sayHi method and put it in it's own variable, as shown below πŸ‘‡?

const sayHi = person1.sayHi;

When you run that, we will just see "hey " with no name.

we see 'hey ' with no name when we run

You can see if we call the sayHi() on the person object, we get the hey message returned with a name.

But when we just call sayHi(), this is equal to the window because there is nothing that the method was bound to on the left-hand side.

That is important. In JavaScript, the this keyword is always defined by where the function is being called, and not where the function is being defined.

So although we defined the sayHi function inside of an object, it's not bound to it unless we call it as a method of an object.

We can use the bind keyword to change where the this keyword is equal to or what is it bound to.

Let's try that by binding the sayHi method back to the original person.

const sayHi = person1.sayHi.bind(person);

The code above tells JavaScript to call a function sayHi and when it's called, it's this keyword is equal to whatever we pass to the bind() method.

Now when you refresh the page, if you were to run sayHi(), you will see that it now says "hey wes bos" instead of just "hey".

That is because we have changed what this will be equal to by binding it to another object.

Why would that be useful?

Sometimes you want to use a method of an object with some other information. Let's say we have a Jenna object, as shown below πŸ‘‡

const jenna = { name: 'Jenna' };

How would we use the sayHi method for this other object when name isn't being passed in as an argument?

not passing name as an argument to sayHi function

That is the difference between object oriented programming and functional programming.

accessing the name property of an object using this keyword

Our sayHi method is object oriented.

If it were to take an argument, that would be much more of a functional approach.

What we can do is we can bind `sayHi`` to Jenna object as shown below.

const sayHi = person.sayHi.bind(jenna);

binding sayHi function to jenna object

bind is a method that lives on all functions and it says change the this keyword to be equal to, in this example, a different object.

You could also manually pass in a name, as shown below.

const sayHi = person.sayHi.bind({ name: 'Harry' });

If you refresh the page and call sayHi() you will see "hey Harry" returned.

Let's look at another example.

document.querySelector and document.querySelectorAll are kind of hard to type.

Let's do an example using that.

Let's say we wanted to make $ a short hand for document.querySelector and $$ for document.querySelectorAll.

You might think you could do something as shown below πŸ‘‡

//QS Example

const $ = document.querySelector();

Now if you refresh the page, open the dev tools console and type $ you will see that it is the querySelector function.

If you try typing in document.querySelector it will be the exact same thing. In fact, they are identical.

querySelector and document.querySelector are exact same thing

Aside: sometimes you see that "[native code]" in the console. That means that is a function that has been implemented by the browser so we are not able to see how it works under the hood because it's implemented in whatever language it's written in.

Now if you wanted to actually select something, you might think you would be able to just do $('p'). However, if you try that you will see that we get an error complaining about "Illegal invocation".

uncaught type error: illegal invocation in console

What is happening here?

Somewhere under the hood in document.querySelector it needs to know where to look for the thing that you are selecting.

Let's demonstrate the reason for that with an example.

<div class="wrapper">
  <p>Hey im in a wrapper</p>
</div>

Now let's say we grabbed the wrapper and did the following πŸ‘‡

const wrapper = document.querySelector('wrapper');
const p = wrapper.querySelector('p');
console.log(p);

selecting a paragraph using querySelector and logging in console

As you can see, that works. We got it.

querySelector is a function and it needs to know what to look inside of for the selector.

The reason it knows where to scope it to, either globally (the document) or in a subset of the DOM, based on what is left of the dot.

So when we try to log $('p'), it doesn't work because there is nothing to the left of the dot. We have taken away the object that it was called against and as a result it is not bound to anything.

Bind method

The way that we can fix that is we can call bind() on it and manually pass it reference to the thing we want it to be equal to.

// by calling bind against querySelector, we say that when the $ function is run, use `document` as the `this` value.
const $ = document.querySelector.bind(document);

Now if you try doing console.log($('p')), you will see that it works now because it has been bound to the function.

This piece of code, document.querySelector.bind(document); does not run the function, it return the function which you can then store in a variable, which in our example is $.

You could also do that with querySelectorAll, as shown below πŸ‘‡

const lookFor = document.querySelectorAll.bind(document);
console.log(lookFor('p'));

You would see that a NodeList is returned to us.

paragraphs as node list returned by lookFor('p')

To reiterate: using bind will change the context of what this is equal to inside of a function or a method.

bind is also useful to prep a function that has arguments sort of "pre-loaded".

Let's demonstrate with an example.

const bill = {
  total: 1000,
  calculate: function(taxRate) {
    return this.total + (this.total * taxRate);
  }
}

const total = bill.calculate(0.13);
console.log(total);

Let's refresh the page to check if it works. You should see 1130 in the console.

total bill in console

If you wanted to take that function and store it in an external variable, you could add the code below πŸ‘‡

const calc = bill.calculate;

If you tried to call calc and pass it 0.13, you would see NaN in the console.

console.log(calc(0.13));

NaN in console log

Why?

Because if we were to log this inside of the calculate method on our bill object, you would see that it is equal to the window.

logging this keyword inside calculate returns window object

The first time around it is equal to our bill object and the second time it was called it was equal to the window.

That is because calc is not bound to anything. We could just bind it to the original bill and that would work.

const calc = bill.calculate.bind(bill);

You could also pass it it's own bill as shown below πŸ‘‡

const calc = bill.calculate.bind({ total: 500 });
console.log(calc(0.13));

In the next example, we will demonstrate how bind can be used to "pre-load" functions with some arguments that need to be called.

Wes likes to think of this as the "check-in online" of functions. What that means is when you bind something, you can pass it additional arguments that line up with the arguments of the function or method.

So if we wanted to pass the tax rate, we could do that in the arguments we pass to bind and not pass it when we call the calc function, as you see in the code below.

const calc = bill.calculate.bind({ total: 500 }, 0.06);
console.log(calc());

total bill in console using bind method

As you can see, it still works! Why?

The additional arguments to bind, the first one will always be the this object, and then the additional ones will line up with the arguments that get passed. If bill.calculate took more arguments, like tipRate, we could pass another value as an argument.

Why is that helpful?

Sometimes, when you are generating functions, for example looping over a list of data, you have access to the data at the time of function creation, and then let's say later you want to call it.

Sometimes it's easier to pre-load the function by passing it what the arguments will be at call time when you are binding it. Then you can just go ahead and take the function and call it from wherever you want because you know the arguments are already included.

Call and Apply methods

Let's move on to the two methods: call and apply.

They work the exact same as bind does with one difference: they will call the function for you.

calc function in console

Call method

Instead of returning, like you see happens above when we call calc, if we just duplicate the line of code where we are declaring calc and modify it like below, let's see what is returned.

const total2 = bill.calculate.call({total:500}, 0.06);
console.log(total2);

You will see we have 530 returned to us.

What happened there?

.bind calls a function, which then needs to be called by itself.

call does the same thing as bind but it will also run the function for you so you don't have to call it.

If you need to bind a function and call it later, use bind.

If you need to bind a function that you want to call immediately you can use call.

Apply method

apply method documentation in mozilla developers docs

Note: While the syntax of this function is almost identical to that of call, the fundamental difference is that call accepts an argument list, while apply accepts a single array of arguments.

If you want to run one of the functions but you don't care what this is equal to, you can just pass null and then after you pass the additional arguments as an array.

In our case, there is only 1 argument.

const total3 = bill.calculate.apply({ total: 325 }, [0.60]);
console.log(total3);

That is not very useful now that we have spread, because you could just spread into call but it's available if you need it.

Let's add 1 more argument to our bill object.

const bill = {
  total: 1000,
  calculate: function(taxRate) {
    return this.total + (this.total * taxRate);
  },
  describe(mealType,drinkType,taxRate) {
    console.log(`Your meal of ${mealType} with a drink of ${drinkType} was ${this.calculate(taxRate)}`);
  }
}

Now we can try running the function in the console.

bill.describe('pizza', 'beer', 0.13);

calling describe method of the bill object in console 17:52

As you can see the function works.

Now if we wanted to run that with call and apply we could do the following.

const myMeal = bill.describe.call({ total: 342 }, 'pizza', 'beer', 0.13);
console.log(myMeal);

calling the same describe method of bill object using new parameters

Why do we pass an object as the first argument to call?

Because that is what this will be equal to, and inside of calculate it looks for this.total so we need an object with a total property.

The additional arguments are mealType, drinkType and taxRate.

If you try running that, you will see that we get an error complaining that "this.calculate is not a function".

What is happening?

on running, we get an error complaining that "this.calculate is not a function"

We called this.calculate but we didn't pass it the this that we wanted.

Let's go modify the myMeal declaration so that it has access to the calculate function.

const myMeal = bill.describe.call(bill, 'pizza', 'beer', 0.13);

calling the modified myMeal function in console

Now it should be working for you.

If you were to apply that instead, we would pass all the arguments as 1 argument.

const myMeal2 = bill.describe.apply(bill, ['pizza', 'beer', 0.13]);
console.log(myMeal2);

That wraps up call, bind, and apply.

When should you use them? When the this value is different from what you have hoped. You won't always need it but it is helpful to know.

Find an issue with this post? Think you could clarify, update or add something?

All my posts are available to edit on Github. Any fix, little or small, is appreciated!

Edit on Github

Syntax Podcast

Hold on β€” I'm grabbin' the last one.

Listen Now β†’
Syntax Podcast

@wesbos Instant Grams

Master Gatsby

Master Gatsby

Building modern websites is tough. Preloading, routing, compression, critical CSS, caching, scaling and bundlers all make for blazing fast websites, but extra development and tooling get in the way.

Gatsby is a React.js framework that does it all for you. This course will teach you how to build your websites and let Gatsby take care of all the Hard Stuffβ„’.

MasterGatsby.com
I post videos on and code on

Wes Bos Β© 1999 β€” 2021