This often appears in the actual development, but many people often cannot accurately judge the direction of this, today I will take you to understand the direction of this problem.

misunderstanding

What is this? This is a function-related mechanism that is automatically defined in the scope of a function. There are two common misunderstandings when we use this:

thisPointing to the function itself

Given the semantics of this, it is easy for developers to mistake this for referring to the function itself. Here is an example:

function foo() {
  this.count++
}

foo.count = 0

for (let i = 0; i < 3; i += 1) {
  foo()
}

console.log(foo.count)
Copy the code

In the example above, function foo wants to count the number of times it is called with its own property count. If this were to point to the function itself, the console would print 3, but when you run the code, it still prints 0, which is sufficient proof that this is pointing to the function itself.

thisRefers to the lexical scope of a function

The idea that this refers to the lexical scope of a function may seem valid in some cases, but this understanding is biased, so the idea is still false.

Binding mechanism

There are two misconceptions about what this refers to. What does this refer to?

This is actually bound only when the function is called, and what it points to depends entirely on how the function is called. There is no point in using the function declaration code to parse the reference to this. Let’s look at the binding rules for this:

The new binding

The new operator also exists in JavaScript, and is normally used in conjunction with functions. Let’s first explain what happens when new is used in conjunction with a function call:

  1. Create a brand new object.
  2. The [[Prototype]] of this new object will match the functionprototypeAssociation.
  3. This new object will be bound to the function callthisOn.
  4. If the function returns no other object, thennewThe function call in the expression automatically returns the brand new object.

Let’s focus on points 1, 3 and 4, for example:

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

var person = new Person('O_c')

console.log(person.name) // O_c
Copy the code

In the above example, the new operator is paired with the Person function, and a new object Person is generated when the function is called, and the Person object is bound to the this of the function call, so when this.name = name is called, Person.name = name.

You now know that the new operator can affect the behavior of the this binding of a function call, which is called the new binding.

According to the binding

Most functions in JavaScript have call(…). And the apply (…). These two methods. Their first argument is an object that they bind to the function call’s this. The operation that directly specifies this to point to is called a display binding.

function foo() {
  console.log(this.a)
}

var obj = {
  a: 2
}

foo.call(obj) / / 2
Copy the code

In the above example, we use call(…) Method to bind an obj object to this on the foo call.

Implicit binding

The implicit binding rule is that when a function reference has a context object, the this of the function call is bound to that context object.

function foo() {
  console.log(this.a)
}

var obj = {
  a: 2.foo: foo
}

obj.foo() / / 2
Copy the code

In the example above, Foo is “contained” in the context object obj, so the implicit binding rule binds the obj object to Foo’s this when the function is called.

Implicit binding is lost

Implicit binding loss often occurs in assignment operations. The context object is lost during assignment operations, so this will be bound unexpectedly. See the following example:

var a = 'global'

function foo() {
  console.log(this.a)
}

var obj = {
  a: 2.foo: foo
}

var bar = obj.foo

bar(); // global
Copy the code

Because functions are reference types, when we assign obj.foo to bar, the bar identifier actually refers directly to the memory address of foo. In this case, calling bar is equivalent to calling foo directly, resulting in the loss of the context object, so the final console prints global.

The default binding

The default binding rule applies to individual function calls, and can also be viewed as the default rule when the above three rules cannot be applied. This binds the function call’s this to the global object.

var a = 2

function foo() {
  console.log(this.a)
}

foo() / / 2
Copy the code

In the example above, we made a separate call to foo, so its this is bound to the global object by the default binding rule.

However, there is a caveat to this rule: when the function body is in strict mode, this is bound to undefined:

var a = 2

function foo() {
  'use strict'
  console.log(this.a)
}

foo() // TypeError: Cannot read property 'a' of undefined
Copy the code

Arrow function

The arrow function is singled out for a reason, because the arrow function does not follow the above four rules, it determines this based on the outer scope, and the binding of the arrow function cannot be changed.

var a = 2;

var foo = () = > {
  console.log(this.a)
}

var obj = {
  a: 4.foo: foo
}

obj.foo() / / 2
Copy the code

In the example above, we are calling the function through the context object obj, but the arrow function is not affected by the implicit binding rule, so the console prints a result of 2.

summary

Today we learned about the binding mechanism of this. To determine the direction of this, we must start from the location of the function call. After finding a function call, you can apply the following four rules in order:

  1. bynewCall to bind to the newly created object.
  2. bycallorapplyCall to bind to the specified object.
  3. Called by the context object and bound to the context object.
  4. Independently called, bound in strict mode toundefinedOtherwise, bind to the global object.

When there are multiple calls to the same function, the application rules are prioritized in the same order.

Finally, remember that the arrow function does not apply the above four rules; its this pointer is determined by its scope.