This chapter focuses on one final detail that is directly relevant to the execution context creation phase — what is this? And what it’s pointing to.

To understand this

Perhaps you’ve seen this in other object-oriented programming languages and know that it points to an object created by a constructor. But in fact, in JavaScript, this stands for more than just the object being created.

Let’s take a look at how the ECMAScript standard specification defines this:

“The this keyword evaluates to The value of The ThisBinding of The current execution context. This keyword represents the value of ThisBinding for the current execution context.”

Then let’s look at MDN’s definition of this:

In most cases, the value of this is determined by how a function is called. In most cases, the value of this depends on how the function is called.

Okay, so if you can read the first two lines you don’t have to go any further. Congratulations!

. I don’t think so, at least I don’t understand these two lines.

Let’s start with an example:

var getGender = function() {
    return people1.gender;
};

var people1 = {
    gender: 'female',
    getGender: getGender
};

var people2 = {
    gender: 'male',
    getGender: getGender
};

console.log(people1.getGender());    // female
console.log(people2.getGender());    // female

Copy the code

what? People2 changed sex, this is not what I wanted, why?

Because getGender() returns (return) people1.gender, the result is ‘female’.

So, if we change getGender a bit:

var getGender = function() {
    return this.gender;
};

Copy the code

At this point, you should get both female and male.

This example shows that even though the getGender methods of People1 and People2 refer to the same getGender function, the results will be different because of the different objects called.

Now we know the first important point, **this is actually a binding that happens when the function is called, and what it points to depends entirely on how the function is called. How does ** distinguish this?

Who is this

After reading the above example, still a little bit confused? Let’s look at how different calls to this affect the value.

Case 1: Global object & call ordinary function

In the global environment, this points to the global object; in the browser, it is the Window object. In the following example, whether in strict mode or not, this refers to a global object.

var x = 1

console.log(this.x)               // 1
console.log(this.x === x)         // true
console.log(this === window)      // true

Copy the code

If a normal function is called in a global context, this also refers to the global object in non-strict mode. In strict mode, this will be undefined. ES5 adds strict mode to make JavaScript run in more restrictive environments. Strict mode eliminates security concerns by disallowing the this keyword to point to global objects.

var x = 1 function fn() { console.log(this); // Window global object console.log(this.x); // 1 } fn();Copy the code

After using strict mode:

Var x = 1 function fn() {console.log(this); var x = 1 function fn() {console.log(this); // undefined console.log(this.x); } fn(); // Cannot read property 'x' of undefined";Copy the code

Case two: call as an object method

We know that a value in an object is a primitive type (primitive type; For example, string, value, Boolean), we will call this newly created thing “property”; If the value inside the object is a function, we call the newly created object a method.

If the function is called as a method of an object, and is called as a method of an object, the this in the function refers to the upper-level object.

var x = 1 var obj = { x: 2, fn: function() { console.log(this); console.log(this.x); }} obj.fn() // obj.fn() prints the result; // Object {x: 2, fn: function} // 2 var a = obj.fn a() // a() Prints: // Window global Object // 1Copy the code

In the above example, we run obj.fn() directly. The object above the call is obj, so this points to obj, resulting in this.x of 2; We then assign the fn method first to variable A, which runs in the global environment, so this points to the global object Window, and this.x is 1.

Let’s look at another example of what this refers to if a function is called by multiple nested objects.

var x = 1
var obj = {
  x: 2,
  y: {
    x: 3,
    fn: function() {
      console.log(this);   // Object {x: 3, fn: function}
      console.log(this.x);   // 3
    }
  }
}

obj.y.fn();      

Copy the code

Why isn’t the result 2? In this case, remember: this always refers to the object above the function directly called, y, and the above example actually executes the following code.

var y = {
  x: 3,
  fn: function() {
    console.log(this);   // Object {x: 3, fn: function}
    console.log(this.x);   // 3
  }
}

var x = 1
var obj = {
  x: 2,
  y: y
}

obj.y.fn();    

Copy the code

Objects can be nested, functions can be nested, if functions are nested, does this change? Let’s explore this with the following code.

var obj = { y: function() { console.log(this === obj); // true console.log(this); // Object {y: function} fn(); function fn() { console.log(this === obj); // false console.log(this); // Window global object}}} obj.y();Copy the code

In function Y, this points to the object above which it was called, obj, which is fine. But in the nested function fn, this does not refer to obj. A nested function does not inherit this from the calling function. When a nested function is called as a function, its this value refers to a global object in non-strict mode, and is undefined in strict mode, so the above example actually executes the following code.

function fn() { console.log(this === obj); // false console.log(this); Var obj = {y: function() {console.log(this === obj); // true console.log(this); // Object {y: function} fn(); } } obj.y();Copy the code

Case 3: Called as a constructor

We can use the new keyword to generate an instance object through the constructor. At this point, this points to the new object.

var x = 1; Function Fn() {this.x = 2; console.log(this); // Fn {x: 2} } var obj = new Fn(); / / obj and Fn (..) Call this to bind console.log(obj.x) // 2Copy the code

Use new to call Fn(..) , constructs a new object and binds it (obj) to Fn(..) Call this in this. It is also worth noting that if the constructor returns a non-reference type (string, number, Boolean, null, undefined), this still points to the new object being instantiated.

var x = 1

function Fn() {
  this.x = 2

  return {
    x: 3
  }
}

var a = new Fn()

console.log(a.x)      // 3

Copy the code

Since Fn() returns an object (reference type), this refers to the object of that return. What if the return is a value of a non-reference type?

var x = 1

function Fn() {
  this.x = 2

  return 3
}

var a = new Fn()

console.log(a.x)      // 2

Copy the code

Case 4: Call and apply method calls

If you want to change the direction of this, use the Call or apply methods. The first argument to both of them specifies that the function is run with this pointing to. If the first argument is not passed (null) or null, undefined, by default this refers to a global object (non-strict mode) or undefined (strict mode).

var x = 1; var obj = { x: 2 } function fn() { console.log(this); console.log(this.x); } fn.call(obj) // Object {x: 2} // 2 fn.apply(obj) // Object {x: 2} // 2 fn.call() // Window global object // 1 fn.apply(null) // Window global object // 1 fn.call(undefined) // Window global object // 1Copy the code

With Call and Apply, if you pass this something other than an object, JavaScript uses constructors to convert it to an object, such as number, and a new number () operation, such as string, The new String() operation will be performed. For example, if you pass a Boolean type, the new Boolean() operation will be performed.

function fn() {
  console.log(Object.prototype.toString.call(this))
}

fn.call('love')      // [object String]
fn.apply(1)          // [object Number]
fn.call(true)          // [object Boolean]

Copy the code

The difference between Call and apply is that the second and subsequent arguments to call are a list of arguments, while the second argument to apply is an array. Both the argument list and the argument array are executed as arguments to the function.

var x = 1

var obj = {
  x: 2
}

function Sum(y, z) {
  console.log(this.x + y + z)
}

Sum.call(obj, 3, 4)       // 9
Sum.apply(obj, [3, 4])    // 9

Copy the code

Case 5: Call the bind method

Calling f.bind(someObject) creates a function with the same body and scope as f, but in this new function, the this of the new function will always refer to the first argument passed in to bind, regardless of how the function was called.

var x = 1 var obj1 = { x: 2 }; var obj2 = { x: 3 }; function fn() { console.log(this); console.log(this.x); }; var a = fn.bind(obj1); var b = a.bind(obj2); fn(); // Window global object // 1 a(); // Object {x: 2} // 2 b(); // Object {x: 2} // 2 a.call(obj2); // Object {x: 2} // 2Copy the code

In the above example, although we tried to reassign this to function A, it still points to the object that bind passed in the first time, even call or apply does not change the fact that it permanently points to the first argument that bind passed in.

Case 6: Arrow function this points to

It is worth mentioning that arrow functions have been added from ES6. Let’s take a look at the description of arrow functions on MDN

An arrow function expression has a shorter syntax than a function expression and does notbind its ownthis,arguments,super, ornew.target. Arrow functions are always anonymous. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

As is clearly stated here, the arrow function does not have its own this binding. The arrow function uses this, which is actually the function or function expression that contains it directly. In the previous example of function nesting in case 2, the nested function does not inherit this from the upper function. What happens if you use the arrow function?

var obj = {
  y: function() {
        console.log(this === obj);   // true
        console.log(this);           // Object {y: function}

      var fn = () => {
          console.log(this === obj);   // true
          console.log(this);           // Object {y: function}
      }
      fn();
  }
}

obj.y() 

Copy the code

Unlike normal functions, this in the arrow function points to obj, because it inherits this from the function at the previous level. You can interpret the arrow function as correcting this. So the arrow function’s this is not determined when it is called, but when it is defined the object is its this.

In other words, the this of the arrow function looks to see if the outer function has a function. If so, the this of the outer function is the this of the inner arrow function; if not, this is the window.

var obj = { y: () => { console.log(this === obj); // false console.log(this); // Window global object var fn = () => {console.log(this === obj); // false console.log(this); // Window global object} fn(); } } obj.y()Copy the code

In the above example, although there are two arrow functions, this depends on the outermost arrow function. Since obj is an object and not a function, this points to the global object called Window.

Like bind, the arrow function is “stubborn.” We can’t use call and apply to change the orientation of this, meaning that the first argument passed in is ignored.

var x = 1 var obj = { x: 2} var a = () => {console.log(this.x) console.log(this)} a.call(obj) // 1 // Window Global objectCopy the code

The above text may seem a bit dry, so take a look at the flow diagram below, which I think summarizes it well and only applies to a single rule.

summary

This article covers several cases to which this refers, and it can be affected by different runtime environments and invocation methods. In general, the reference to the function this depends on the object on which the function is currently called, that is, when it is executed. In this section, you need to master:

  • This points to a global object;
  • The difference between this in strict mode and non-strict mode;
  • Cases to which this refers when a function is called as a method of an object;
  • The difference between pointing to this as a constructor and returning it or not;
  • Use call and apply to change the object on which the function is called;
  • The reference to this in the function created by bind;
  • The this in the arrow function points to.