A. this B. this C. this D. this

The this keyword is one of the most complex mechanisms in javascript. It is a special keyword that is automatically defined in the scope of all functions.

Why this

function identify() {
  return this.name.toUpperCase()
}

function speak() {
  var greeting = "Hello, I am " + identify.call(this)
  console.log(greeting)
}

var me = {
  name: "Kyle"
}

var you = {
  name: "Reader"
}

identify.call(me) // KYLE
identify.call(you) // READER

speak.call(me) // Hello, I am KELE
speak.call(you) // Hello, I am READER

Copy the code

This code can reuse the identify() and speak() functions in different context objects (me and you) without writing a different version of the function for each object

This is the wrong perception

  1. Point to their own

It’s easy to read this as referring to the function itself, so why reference itself from within the function? The common reason is recursion.

The code below shows that this does not refer to the function itself as expected

function foo(num) {
  console.log("foo: " + num)
  // Records how many times foo is called
  this.count++
}

foo.count = 0

for (var i = 0; i < 10; i++) {
  if (i > 5) {
    foo(i)
  }
}

// foo: 6
// foo: 7
// foo: 8
// foo: 9

// How many times is foo called?
console.log(foo.count) / / 0
Copy the code

The foo() function is actually called four times, but foo.count is still 0

Foo. Count = 0 does add an attribute count to function object foo, but this in function code this.count does not refer to that function object.

The developer in charge is going to say, what count am I adding? If you dig a little deeper, you’ll see that this code inadvertently creates a global variable, count, which has a value of NaN.

  1. Point to the function scope

This does not in any case refer to the lexical scope of a function. Inside JavaScript, a scope is indeed similar to an object in that its visible identifiers are its attributes, but a scope object cannot be accessed through JavaScript code; it exists inside the JavaScript engine.

Consider the following code, which tries to cross the border by using this to implicitly reference the lexical scope of a function:

function foo() {
  var a = 2
  this.bar()
}

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

foo() // ReferenceError: a is not defined
Copy the code

There is more than one error in this code. First, the code attempts to reference the bar() function via this.bar(). This is a non-starter. The most natural way to call bar() is to omit the preceding this and refer directly to the identifier using a lexical reference.

In addition, this code attempts to use this to connect the lexical scopes of foo() and bar() so that bar() can access variable A of foo(), which is impossible. You can’t use this to refer to something inside a lexical scope

What is this

With some misunderstanding out of the way, what mechanism is this

The context of this depends on the conditions under which the function is called. The binding of this has nothing to do with where the function is declared, but only how the function is called.

When a function is called, an active record (sometimes called the execution context) is created. This record contains information about where the function was called (call stack), how the function was called, the parameters passed in, and so on. This is one of the properties of the record that is used during the execution of the function.

Binding rules

How does the call location determine the binding object for this during the execution of a function

  1. The default binding
function foo() {
  console.log(this.a)
}
var a = 2
foo() / / 2
Copy the code

When foo() is called, this.a is resolved to a global variable a, and the default binding is applied when the function is called, so this points to the global object.

  1. Implicit binding

Whether the calling location has a context object, or is owned or contained by an object.

function foo() {
  console.log(this.a)
}
var obj = {
  a: 2.foo: foo
}
obj.foo() / / 2
Copy the code

Notice first how foo() is declared and then added to OBj as a reference property, but whether defined directly in OBj or first and then added as a reference property, this function is not strictly an OBj object.

However, the calling location references the function using the OBJ context, so you can say that the obJ object “owns” or “contains” the function when it is called.

  1. According to the binding

Use the call (…). Or apply (…). methods

function foo() { 
  console.log( this.a );
}
var obj = { a:2 };
foo.call( obj ); / / 2
Copy the code

Through the foo. Call (..) , we can force foo to bind its this to obj when calling foo.

  1. The new binding

When a function is called with new, or when a constructor call occurs, the following operations are performed automatically

  1. Create (or construct) a brand new object.
  2. The new object will be connected by the implementation [[prototype]].
  3. This new object is bound to the function call’s this.
  4. If the function returns no other object, the function call in the new expression automatically returns the new object.
function foo(a) {
  this.a = a
}
var bar = new foo(2)
console.log(bar.a) / / 2
Copy the code

Call foo(..) with new , we construct a new object and bind it to foo(..). Call to this

priority

Now that we know that this is bound to four rules in a function call, all you need to do is find where the function is called and determine which rule should be applied, in this order:

  1. Is the function called in new? If so, this binds to the newly created object
  2. Whether the function shows the binding via call, apply, and if so, this binds to the specified object
  3. Is the function called in a context object (implicitly bound)? If so, this binds to that context object
  4. If neither, use the default binding

This lexical

The four rules described above can already contain all normal functions, but ES6 introduces a type of function that cannot use these rules: arrow functions

Let’s look at the lexical scope of the arrow function:

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

var obj1 = { a: 2 }
var obj1 = { a: 3 }

var bar = foo.call(obj1)
bar.call(obj2) // 2, not 3
Copy the code

The arrow function created inside foo() captures this that calls foo(), and since foo() ‘s this is bound to obj1, and bar (which references the arrow function)’ s this is bound to obj1, the binding of the arrow function cannot be modified.

conclusion

If you want to determine the this binding of a running function, you need to find where the function is called directly, and then you can apply the following four rules in sequence.

  1. Called by new? Bind to the newly created object
  2. Called by call, apply, or bind? Binds to the specified object
  3. Called by a context object? Bind to that context object
  4. Bind globally by default

The arrow function in ES6 does not use these four binding rules. Instead, it determines this based on the current lexical scope. Specifically, the arrow function inherits the this binding of the outer function call.