Recently, I have been reading the JavaScript volume that you do not know, and I think the explanation of this is very wonderful. This is one of the core concepts in JavaScript, and some of you may find it a little vague and scary. The reason for this is that there are so many articles discussing this that we feel like this is a ghost

If you haven’t understood this yet, or are confused about it, this article is for you, and if you are relatively familiar with it, you can use it as a review to reinforce your knowledge

This article, is a reading notes, of course, also added a lot of my personal understanding, I think it must be helpful to you

Execution context

Before we understand this, what is an execution context

In short, an execution context is an abstraction of the context in which JavaScript code is evaluated and executed. Whenever Javascript code is running, it is running in the execution context

There are three types of execution context in JavaScript

  • Global execution context – This is the default or underlying context. Any code that is not inside a function is in the global context. It does two things: create a globalwindowObject (in the case of the browser) and SettingsthisIs equal to the global object. There is only one global execution context in a program
  • Function execution context – Every time a function is called, a new context is created for that function. Each function has its own execution context, which is created when the function is called. There can be any number of function contexts
  • evalFunction execution context – where the function is executedevalThe code inside the function also has its own execution context, which is not often used by JavaScript developersevalSo I won’t discuss it here

The first thing we need to conclude here is that in both non-strict mode and strict mode this refers to the top-level object (in the browser, window)

console.log(this= = =window); // true
'use strict'
console.log(this= = =window); // true
this.name = 'vnues';
console.log(this.name); // vnues
Copy the code

Our discussion later will focus more on the function execution context

What exactly is this? Why use this

This is bound at run time, not at write time, and its context depends on various conditions when the function is called

Keep in mind that the binding to this has nothing to do with where the function is declared, but only with how the function is called

When a function is called, an activity record (sometimes called the execution context) is created. This record will contain information about where the function was called (the call stack), how the function was called, the arguments passed, and so on. This is one of the recorded properties that will be used during the execution of the function

Take a look at an example to understand why we use this. Sometimes, we need to implement code like this:

function identify(context) {
  return context.name.toUpperCase();
}
function speak(context) {
  var greeting = "Hello, I'm " + identify(context);
  console.log(greeting);
}
var me = {
  name: "Kyle"
};
speak(me); / / hello, I am KYLE
Copy the code

The problem with this code is that you need to display and pass context objects. If your code gets more complex, this approach will make your code look messy. Using this is more elegant

var me = {
  name: "Kyle"
};

function identify() {
  return this.name.toUpperCase();
}
function speak() {
  var greeting = "Hello, I'm " + identify.call(this);
  console.log(greeting);
}
speak.call(me); KYLE: Hello, this is KYLE
Copy the code

Four binding rules for this

Let’s look at the binding rules in the context of a function. There are four types of binding rules

  • The default binding
  • Implicit binding
  • Explicitly bound
  • newThe binding

The default binding

The most common type of function call is the stand-alone function call, which also has the lowest priority. This refers to the global object. Note: If strict mode is used, the default binding cannot be used for global objects, so this is bound to undefined, as shown below

var a = 2;  // Declare variables in global objects
function foo() {
  console.log(this.a);   / / output a
}

function bar() {
  'use strict';
  console.log(this); // undefined
}
foo();
bar();
Copy the code

Implicit binding

The binding to this has nothing to do with where the function is declared, but only with how the function is called

Here’s an example:

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

When obj.foo() is called, this points to the obj object. When a function reference has a context object, the implicit binding rule binds this in the function call to that context object. Because this is bound to obj when calling foo(), this.a and obj.a are the same

Remember: only the top or last level of the object property reference chain affects the call location

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

Indirect reference

Another thing to note is that it is possible (intentionally or unintentionally) to create an “indirect reference” to a function, in which case calling the function will apply the default binding rules

function foo() { console.log(this.a); } var a = 2; var o = { a: 3, foo: foo }; var p = { a: 4 }; o.foo(); // 3 (p.foo = o.foo)(); / / 2Copy the code

Another thing to note is that it is possible (intentionally or unintentionally) to create an “indirect reference” to a function, in which case calling the function will apply the default binding rules

The return value of the assignment expression p.foo = o.foo is a reference to the target function, so the call location is foo() instead of p.foo() or o.foo(). As we said before, default bindings will apply here

According to the binding

When analyzing implicit binding, we must bind this indirectly (implicitly) to an object by including a property within the object that points to a function and indirectly refers to the function through that property. So what if we want to force a function call on an object instead of including a function reference inside the object?

Javascript provides the apply, Call, and bind methods that we can implement

The difference is that call() and apply() execute functions immediately and take different forms of arguments:

  • call(this, arg1, arg2, ...)
  • apply(this, [arg1, arg2, ...] )

Bind (), on the other hand, creates a new wrapper function and returns it, rather than executing it immediately

  • bind(this, arg1, arg2, ...)

Here’s an example:

  function foo(b) {
    console.log(this.a + ' ' + b);
  }
  var obj = {
    a: 2.foo: foo
  };
  var a = 1;
  foo('Gopal'); // 1Gopal
  obj.foo('Gopal'); // 2Gopal
  foo.call(obj, 'Gopal'); // 2Gopal
  foo.apply(obj, ['Gopal']); // 2Gopal
  let bar = foo.bind(obj, 'Gopal');
  bar(); // 2Gopal
Copy the code

This is ignored

If you pass null or undefined to call, apply, or bind as the binding object to this, these values will be ignored and the default binding rules will be applied

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

Use this usage to apply(..) To “expand” an array and pass it as an argument to a function. Similarly, bind(..) You can curlize parameters (pre-set some parameters)

  function foo(a, b) {
    console.log("a:" + a + ", b:" + b);
  }
  // Expand the array to arguments
  foo.apply(null[2.3]); // a:2, b:3
  / / use the bind (..) Currying
  var bar = foo.bind(null.2);
  bar(3); // a:2, b:3
Copy the code

The new binding

When we use the constructor new for an instance, what does this refer to for that instance?

Let’s first look at what happens when we call a function using new, or when we call a constructor, as follows:

  • Create (or construct) an entirely new object
  • This new object will be executed [[prototype]] to connect the object (instance)__proto__And the constructorprototypeThe binding
  • The new object will be bound to the function callthis
  • If the function returns no other object, then the function call in the new expression automatically returns the new object

The principle is similar to the following:

function create (ctr) {
    // Create an empty object
    let obj = new Object(a)// Link to the prototype object of the constructor
    let Con = [].shift.call(arguments)
    obj.__proto__ = Con.prototype
    / / bind this
    let result = Con.apply(obj, arguments);
    // If an object is returned, return the object directly, otherwise return the instance
    return typeof result === 'object'? result : obj;
}
Copy the code

Let result = con.apply (obj, arguments); What this actually means is that the new object will be bound to the function call this

function Foo(a) { this.a = a; } var bar = new Foo(2); console.log(bar.a); / / 2Copy the code

Special case — arrow function

The four rules we introduced earlier can already contain all normal functions. But ES6 introduces a special type of function that doesn’t work with these rules: arrow functions

Instead of using the four standard rules for this, the arrow function determines this based on the outer (functional or global) scope in which it is defined. That is, the arrow function doesn’t create its own this, it just inherits this from the previous level of its scope chain

function foo() {
  // Return an arrow function
  // this inherits from foo()
  return (a) = > {
    console.log(this.a); }};var obj1 = {
  a: 2
};
var obj2 = {
  a: 3
};
var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是 3 !
Copy the code

The arrow function created inside foo() captures the this of foo() when called. Since foo() ‘s this is bound to obj1 and bar(referring to the arrow function)’ s this is bound to obj1, the arrow function ‘s binding cannot be modified. (New doesn’t work either!)

Summary — This priority

Determine whether it is an arrow function. If it is, follow the rules of the arrow function

Otherwise, to determine the this binding of a running function, you need to find where the function was called directly. Once found, the following four rules can be applied in order to determine the binding object for this

  1. bynewCall? Bind to the newly created object
  2. bycallorapply(orbind) call? Bind to the specified object
  3. Called by the context object? Bind to that context object
  4. Default: bound to in strict modeundefinedOtherwise it is bound to a global object

As shown in the figure below:

reference

  • Understand execution context and execution stack in JavaScript

  • You don’t know the JavaScript roll