preface

As a key word in JS language, the complex orientation of this is often confused by beginners, and is also one of the questions frequently asked by interviewers. Only by understanding this orientation in JS, can we be regarded as stepping into the threshold of JS language. This paper will sort out different scenarios of this orientation. And how to judge this direction better and more accurately, so that the interview will not be a headache for this direction.

Understand the key elements that this points to

In ES5, the key to determine the direction of this lies in this sentence. This always refers to the object that calls it. There is only one important point to understand this sentence, who called this.

This call

This is divided into three cases: global object, current object, or arbitrary object; Deciding which case you are in depends entirely on how the function is called

This refers to the object on which the function is called, depending on how the function is called. So it’s easy to understand that this points, and when you look at the first response to the problem of this pointing, just look at who called this. For example, if function A uses this, find which object called function A.

The trouble is to find out which object calls the function using this, because functions can be called in many ways in JS.

  1. Called as a normal function
  2. Called as an object method
  3. useapplycallcall
  4. Called as a constructor

The problem then becomes simply to determine which object the function is called by. Here are four different function calls (for ES5)

Called as a normal function

var a = 'arzh';
function thisName() {
    console.log(this.a);
}
thisName(); //'arzh'
console.log(window);
Copy the code

Let’s take thisName as an example. When called as a normal function, mounted globally, This. A is equivalent to window.a, because global variables defined in ES5 become properties of the window

window.a === this.a === arzh

If I change the function to something like this

var a = 'arzh';
function thisName() {
    var a = 'arzh1'
    console.log(this.a);
}
thisName(); //'arzh'
Copy the code

This is easy to understand because this always refers to window, so the a variable in window will always be arzh, because thisName does not override the a variable on window.

Let’s change the function again

var a = 'arzh';
function thisName() {
    a = 'arzh1'
    console.log(this.a);
}
thisName(); //'arzh1'
Copy the code

“ThisName” is called by “window”, “this” still refers to “window”, “thisName” is called by “window”, “thisName” is called by “window”, “thisName” is called by “window”, “thisName” is called by “window”, “thisName” is called by “window”, “thisName” is called by “window”, “thisName” is called by “window”. So this.a becomes the string that defines the a variable for the last time, namely arzh1;

Called as an object method

This refers to the object that calls the function (method). This refers to the object that calls this. Let’s look at it in code

var a = 'arzh';
var b = {
    a : 'arzh1',
    fn : function() {
        console.log(this.a)
    }
}
b.fn(); //'arzh1'
Copy the code

A === b.a === ‘arzh1’

Modify the function as follows:

var a = 'arzh';
var b = {
    a : 'arzh1',
    fn : function() {
        console.log(this.a)
    }
}
window.b.fn(); //'arzh1'
Copy the code

Window calls b, and B calls fn. Essentially, fn is still called by B, so this always points to B and prints arzh1. If you remove the a property from B, then theoretically you should print undefined, because this is still on B. There is no a property in B. The code is as follows:

var a = 'arzh';
var b = {
    //a : 'arzh1',
    fn : function() {
        console.log(this.a)
    }
}
window.b.fn(); //undefined
Copy the code

Let’s move on

var a = 'arzh';
var b = {
    a : 'arzh1',
    fn : function() { console.log(this.a) } } var fn1 = b.fn; //fn is not called fn1(); //'arzh'
Copy the code

In the same case, the b object assigns fn to the fn1 variable, so fn is not called by anyone, but is simply assigned. The fn method is called only after the fn1 method is executed, so fn1 is mounted on the global window object, that is, the window object is called fn1, so fn1 refers to window, and arzh is output.

So what if this is the case

var a = 'arzh';
var b = {
    a : 'arzh1',
    fn : function() {setTimeout(function() {
            console.log(this.a)
        },100)
    }
}
b.fn(); //arzh
Copy the code

It can be seen from the above that the setTimeout function is executed in B.fein (), and the this in setTimeout refers to the window object. This is because the code called by setTimeout() runs in a completely separate execution environment from the function. For those familiar with EventLoop, the setTimeout function actually calls other threads provided by the browser. When the JS main thread is finished, the function of the task queue is called, namely the setTimeout function. SetTimeout is called on the window. So this naturally points to the window.

Call using apply or call

The apply and call functions can be used to change the this reference. For details, see Apply and call. In this paper, call is used to explain the difference between the apply function and call function, because the difference between the apply function and call function is basically the difference between the passed parameters (the second parameter of apply is an array, and the second parameter of call is not an array). Let’s just look at the code

var a = 'arzh';
var b = {
    a : 'arzh1',
    fn : function() {
        console.log(this.a)
    }
}
var c = {
    a : 'arzh2'
}
b.fn(); //arzh1
b.fn.call(window); //arzh
b.fn.call(c); //arzh2
Copy the code

As you can see from the code, call points this directly to the first argument passed in. We’re just using the result here to figure out that call can change this, so the question is, how does the call function change this, and we can look at a simple implementation of call

Function.prototype.call = functionContext.fn = this; context.fn = this; context.fn = this; context.fn = this; context.fn(); delete context.fn; }Copy the code

Call calls refer to this as an object if the first argument passed to call is an object and the method is called directly. Let’s take the above b.f.call (window) to illustrate

  1. contextThat’s incomingwindow
  2. window.fn = this = window.fn
  3. performcontext.fn => window.fn() => window.fn()

Called as a constructor

Constructors in JavaScript are also special. Constructors, in fact, generate a new object through this function, in which case this refers to the new object; If not called with new, it is the same as a normal function

We know that in JS normal functions can be used directly as constructors, using new to generate new objects

function Name(b) {
    this.b = b;
    console.log(this)
}
var o = new Name('arzh');
//Name {b: "arzh"}

function Name(b) {
    this.b = b;
    console.log(this)
}
Name('arzh')
//Window
Copy the code

You can see that if the object is generated by new, this refers to the already generated object.

Let’s analyze the specific reason: here we quote hu Yu’s great new implementation, the specific implementation process can be seen here

function objectFactoryVar obj = new Object(); Constructor = [].shift.call(arguments); // obj.__proto__ = Constructor. Prototype; // Function Constructor. Apply (obj, arguments); // Returns the successfully created objectreturn obj;
};
Copy the code

The apply function is used to construct the object. One of the parameters of apply is the object obj that needs to be generated. Based on the above use of apply or call, it is not difficult to understand why this refers to the new object when using new.

Arrow function this points to (ES6)

In ES5, the reference to this can be simply summed up in one sentence: this refers to the caller of the function. So in ES6 this statement is actually not true under the arrow function. Let’s look at this example

var a = 'arzh';
var b = {
    a : 'arzh1',
    fn : function() {
        console.log(this.a)
    }
}
b.fn(); //'arzh1'
Copy the code
var a = 'arzh';
var b = {
    a : 'arzh1',
    fn : () => {
        console.log(this.a)
    }
}
b.fn(); //'arzh'
Copy the code

From these two examples, it is obvious that the “this” pointer in the arrow function is different from the “this” pointer in the normal function.

The this object inside the function is the object at which it is defined, not used

So in ES6, the reference to this is fixed, the scope in which the this binding is defined, not the scope in which the runtime is defined

In the example above, the arrow function outputs arzh because the object does not constitute a separate scope, resulting in the scope defined by the FN arrow function being global

This is fixed, not because the arrow function has a mechanism to bind this, but because the arrow function does not have its own this, so the inner this is the outer code block’s this. Since it doesn’t have this, it can’t be used as a constructor, so you can’t use call(), apply(), or bind() to change the direction of this

conclusion

In fact, this is not difficult to point to, more troublesome scenes, as long as the heart to determine which object called the function, you can quickly determine the point of this, I hope to read this article, for you to determine this point has some useful

Refer to the article

[1] Ruan Yifeng blog ES6 tutorial [2] Hu Yu Blog New Simulation implementation