• WTF is this – Understanding the this keyword, call, apply, and bind in JavaScript
  • Tyler McGinnis
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: CoolRice
  • Proofreader: Zhou Jiawei

One of the most misunderstood aspects of JavaScript is the this keyword. In this article, you will learn four rules and figure out what the this keyword refers to. Implicit, explicit, new, and Window bindings. As you introduce these techniques, you’ll also learn some of the other confusing parts of JavaScript, such as the.call,.apply,.bind, and new keywords.

video

  • YouTube video: Youtu.be /zE9iro4r918

The body of the

Before diving into JavaScript’s this keyword, it’s worth taking a step back and looking at why this is important. This allows functions to be reused in different contexts. In other words, the “this” keyword allows you to decide which object should be the focus when a function or method is called. Everything we’ve talked about since has been based on this idea. We want to be able to reuse functions or methods in different contexts or in different objects.

The first thing we need to focus on is how to judge references to the this keyword. The first and most important question you need to ask yourself when trying to answer this question is “Where is this function called?” . The only way to determine what this refers to is to see where the method using the this keyword is called.

To illustrate this with an example you are already familiar with, let’s say we have a greet method that takes a name parameter and displays an alert box with a welcome message.

function greet (name) {
  alert(`Hello, my name is ${name}`)}Copy the code

What would you say if I asked you what the greet will warn you about? It’s impossible to know the answer just by giving the function definition. To know what name is, you have to look at the greet function.

greet('Tyler')
Copy the code

The same goes for determining what the this keyword refers to, and you can even treat this as a normal function argument — it changes depending on how the function is called.

Now we know that in order to determine a reference to this we must first look at the function definition. When we actually look at the function definition, we set up four rules to find references

  1. Implicit binding
  2. Explicitly bound
  3. The new binding
  4. The window binding

Implicit binding

Remember, the goal here is to look at the function definition that uses the this keyword and determine what this points to. The first and most common rule for performing a binding is called implicit binding. 80% of the time it will tell you what the this keyword refers to.

Let’s say we have an object like this

const user = {
  name: 'Tyler',
  age: 27,
  greet() {
    alert(`Hello, my name is ${this.name}`)}}Copy the code

Now, if you call the greet method on the User object, you use the dot.

user.greet()
Copy the code

This brings us to the main point of implicit binding rules. To determine references to this, the function is called by looking to the left of the notation. If there is a dot, look at the object to the left of the dot, which is a reference to this.

In the example above, the fact that user is “to the left of the dot” means that this refers to the User object. So it’s as if the JavaScript interpreter inside the greet method changed this to user.

greet() {
  // alert(`Hello, my name is ${this.name}`)
  alert(`Hello, my name is ${user.name}`) // Tyler
}
Copy the code

Let’s look at a similar but slightly more advanced example. Now our object will not only have name, age, and greet, but will be added with mother, which will also have name and greet.

const user = {
  name: 'Tyler',
  age: 27,
  greet() {
    alert(`Hello, my name is ${this.name}`)
  },
  mother: {
    name: 'Stacey'.greet() {
      alert(`Hello, my name is ${this.name}`)}}}Copy the code

Now the question becomes what does each of the following function calls warn about?

user.greet()
user.mother.greet()
Copy the code

Whenever we decide on a reference to this, we need to look at the call and confirm what is “to the left of the dot”. The first call, user to the left of the dot, means that this will refer to user. In the second call, mother to the left of the dot means that this refers to mother.

user.greet() // Tyler
user.mother.greet() // Stacey
Copy the code

As mentioned earlier, there is an object “to the left of the dot” about 80% of the time. That’s why “look to the left of the dot” is the first thing you do when determining what this points to. But what if there’s no point? Which brings us to our next rule —

Explicitly bound

If the greet function is not a function of the user object, it is a separate function.

function greet () {
  alert(`Hello, my name is ${this.name}`)
}

const user = {
  name: 'Tyler',
  age: 27,
}
Copy the code

We know that in order to determine the reference to this we must first look at where the function was called. How do we call the greet method with this pointing to user? . We can no longer use user.greet() simply because there is no greet method for user. In JavaScript, each function contains a method that allows you to solve exactly this problem. This method is called Call.

“Call” is a method for every function that allows you to specify a context for a function when it is called.

With this in mind, you can use user as a context when calling greet.

greet.call(user)
Copy the code

Again, call is a property that every function has, and the first argument passed to it is the context in which the function is called. In other words, this will refer to the first argument passed to call.

This is the basis of rule 2 (show binding), because we explicitly specify a reference to this (using.call).

Now let’s make a small change to the greet method. What if we want to pass some parameters? Not just their name, but the language they know. It looks like this

function greet (lang1, lang2, lang3) {
  alert(`Hello, my name is ${this.name} and I know ${lang1}.${lang2}, and ${lang3}`)}Copy the code

Now in order to pass these arguments to the function called with.call, you need to pass them in one by one after specifying the context (the first argument).

function greet (lang1, lang2, lang3) {
  alert(`Hello, my name is ${this.name} and I know ${lang1}.${lang2}, and ${lang3}`)
}

const user = {
  name: 'Tyler',
  age: 27,
}

const languages = ['JavaScript'.'Ruby'.'Python']

greet.call(user, languages[0], languages[1], languages[2])
Copy the code

The.call method works, showing how to pass arguments to a function called with.call. However, you may have noticed that you have to pass elements of the languages array one by one, which is somewhat annoying. It would be nice if we could take the entire array as a second argument and have JavaScript automatically expand it for us. The good news is that this is what.apply does. .apply is essentially the same as.call, but instead of passing parameters one by one, you can pass them in arrays and.apply will automatically expand for you in functions.

So now with.apply, our code can change to the following, and everything else stays the same.

const languages = ['JavaScript'.'Ruby'.'Python']

// greet.call(user, languages[0], languages[1], languages[2])
greet.apply(user, languages)
Copy the code

So far, we’ve learned about the “explicit binding” rules for.call and.apply, which call methods that let you specify what this points to within a method. The final part of this rule is.bind. .bind is identical to.call except that it does not call the function immediately, but returns a new function that can be called later. So, if we look at the code we wrote earlier and use.bind instead, it looks something like this

function greet (lang1, lang2, lang3) {
  alert(`Hello, my name is ${this.name} and I know ${lang1}.${lang2}, and ${lang3}`)
}

const user = {
  name: 'Tyler',
  age: 27,
}

const languages = ['JavaScript'.'Ruby'.'Python']

const newFn = greet.bind(user, languages[0], languages[1], languages[2])
newFn() // alerts "Hello, my name is Tyler and I know JavaScript, Ruby, and Python"
Copy the code

The new binding

The third rule for determining this references is the new binding. If you’re not familiar with the new keyword in JavaScript, every time you call a function with new, the JavaScript interpreter creates a new object underneath and calls it this. If you call a function with new, this will naturally refer to the new object created by the interpreter.

functionUser (name, age) {/* JavaScript creates a new object 'this' underneath, which proxies for properties that are not on the User prototype chain. If a function is called with the new keyword, this points to the new object created by the interpreter. */ this.name = name this.age = age } const me = new User('Tyler', 27)
Copy the code

The window binding

Suppose we have the following code

function sayAge () {
  console.log(`My age is ${this.age}`)
}

const user = {
  name: 'Tyler',
  age: 27
}
Copy the code

As mentioned earlier, if you want to call sayAge in the context of user, you can use.call,.apply, or.bind. But what happens if, instead of using these methods, we call sayAge directly as usual?

sayAge() // My age is undefined
Copy the code

Not surprisingly, you get My name is undefined because this.age is undefined. Things are starting to get magical. This is actually because there is nothing to the left of the dot, and we don’t use the.call,.apply,.bind, or new keywords, JavaScript will default this to the window object. This means that if we add age to the window object and call sayAge again, this.age will no longer be undefined and will become the age value of the window object. Don’t believe me? Let’s run this code

window.age = 27

function sayAge () {
  console.log(`My age is ${this.age}`)}Copy the code

Amazing, isn’t it? That’s why rule 4 is the Window binding. If none of the other rules are met, JavaScript defaults this to the window object.


In the strict mode added to ES5, JavaScript does not default this to pointing to the window object, but correctly keeps this undefined.

'use strict'

window.age = 27

function sayAge () {
  console.log(`My age is ${this.age}`)
}

sayAge() // TypeError: Cannot read property 'age' of undefined
Copy the code

So, putting all the rules into practice, whenever I see the this keyword inside a function, these are the steps I take to determine its reference.

  1. See where the function is called.
  2. Is there an object to the left of the dot? If so, it is a reference to “this”. If not, proceed to step 3.
  3. Is this function called with “call”, “apply”, or “bind”? If so, it explicitly specifies a reference to “this”. If not, proceed to Step 4.
  4. Is this function called with “new”? If so, “this” points to the newly created object of the JavaScript interpreter. If not, proceed to Step 5.
  5. Is it in strict mode? If so, “this” is undefined. If not, proceed to step 6.
  6. JavaScript is weird because “this” points to the “window” object.

Note: many comments do not mention the arrow function, so the translator has written a special supplement. If you need to understand, please move to also discuss the arrow function this pointing problem and related.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.