Hey, do you really understand this?

  Front end, javascript, node.js, Programmer

This keyword is one of the most complicated mechanisms in JavaScript. It is a special keyword that is automatically defined in the scope of all functions. However, I believe many JsvaScript developers are not very clear about what it refers to. I heard that you know this very well, is it true?

Please answer the first question first: how to judge exactly what this means? [High Frequency Interview Questions]


[The picture comes from the Internet and is deleted]

Look at another question, what is the value printed by the console? [Browser Operating Environment]

var number = 5;
var obj = {
    number: 3,
    fn1: (function () {
        var number;
        this.number *= 2;
        number = number * 2;
        number = 3;
        return function () {
            var num = this.number;
            this.number *= 2;
            console.log(num);
            number *= 3;
            console.log(number);
        }
    })()
}
var fn1 = obj.fn1;
fn1.call(null);
obj.fn1();
console.log(window.number);

If the result of your thinking is the same as that of your browsing, and the basis for each step is clear, then you can choose to continue reading or close this page and have fun. If you are partially blind or not sure about your answer, please continue to read.

After all, it takes an hour or two to fully understand this, which is a very worthwhile thing, isn’t it?

This article will explain the binding rules of this in detail, and analyze the above two questions at the end.

Why do you want to learn this?

First of all, why should we learn this?

  1. This is used very frequently. If we don’t understand this, it will be very difficult to look at other people’s code or source code.
  2. In my work, I abused this and didn’t understand what this meant, which led to problems, but I didn’t know what went wrong. [In the company, I have helped at least 10 developers deal with this problem]
  3. Reasonable use of this allows us to write concise and reusable code.
  4. High frequency interview questions, the answer is not good, I’m sorry, go out right turn, don’t send.

No matter for what purpose, we need to make this knowledge point clear.

OK,Let’s go!

What is this?

To get back to the point, what is this? First of all, remember that this is not about yourself! This is a pointer to the object that calls the function. We all know this sentence, but most of the time, we may not be able to judge exactly what this means. It is as if we have heard many truths but still have a hard life. Today we will not discuss how to live a good life, but I hope that after reading the following, you can see at a glance what this means.

In order to see at a glance what this refers to, we first need to know what the binding rules of this are.

  1. Default binding
  2. Implicit binding
  3. Hard binding
  4. New binding

You may or may not have heard the above nouns, but after today, please remember them firmly. We will analyze it one by one.

Default binding

The default binding, which is used when other binding rules cannot be applied, is usually an independent function call.

function sayHi(){
    console.log('Hello,', this.name);
}
var name = 'YvetteLau';
sayHi();

When calling Hi (), the default binding is applied. this points to the global object (in non-strict mode). in strict mode, this points to undefined. there is no this object on undefined and an error will be thrown.

If the above code runs in a browser environment, the result is Hello,YvetteLau

However, if you run in a node environment, the result is Hello,undefined. this is because name in node is not hung on a global object.

In this article, unless otherwise specified, the default is the browser environment execution result.

Implicit binding

The function call is triggered on an object, that is, there is a context object at the call location. The typical form is XXX.fun (). Let’s look at a code:

function sayHi(){
    console.log('Hello,', this.name);
}
var person = {
    name: 'YvetteLau',
    sayHi: sayHi
}
var name = 'Wiliam';
person.sayHi();

The printed result is Hello,YvetteLau.

SayHi function is declared externally and strictly does not belong to person. however, when sayHi is called, the calling position will use the person’s context to refer to the function. implicit binding will bind this in the function call (i.e. this in the sayHi function in this example) to this context object (i.e. person in this example)

It should be noted that only the last layer in the object attribute chain will affect the call location.

function sayHi(){
    console.log('Hello,', this.name);
}
var person2 = {
    name: 'Christina',
    sayHi: sayHi
}
var person1 = {
    name: 'YvetteLau',
    friend: person2
}
person1.friend.sayHi();

The result: Hello, Christina.

Because only the last layer will determine what this refers to, no matter how many layers, when judging this, we only focus on the last layer, namely friend here.

Implicit binding has a big trap, binding is easily lost (or misleading, we think what this refers to, but in fact it is not).

function sayHi(){
    console.log('Hello,', this.name);
}
var person = {
    name: 'YvetteLau',
    sayHi: sayHi
}
var name = 'Wiliam';
var Hi = person.sayHi;
Hi();

The result: Hello,Wiliam.

Why is this? Hi directly points to sayHi’s reference. When calling, it has nothing to do with person. In view of such problems, I suggest that all you need to do is to firmly continue this format: XXX.fn (); If there is nothing before fn (), then it is definitely not an implicit binding, but it is not necessarily the default binding. There is a little doubt here, and we will talk about it later.

In addition to the above loss, the loss of implicit binding occurs in callback functions (event callbacks are also one of them). Let’s look at the following example:

function sayHi(){
    console.log('Hello,', this.name);
}
var person1 = {
    name: 'YvetteLau',
    sayHi: function(){
        setTimeout(function(){
            console.log('Hello,',this.name);
        })
    }
}
var person2 = {
    name: 'Christina',
    sayHi: sayHi
}
var name='Wiliam';
person1.sayHi();
setTimeout(person2.sayHi,100);
setTimeout(function(){
    person2.sayHi();
},200);

The result is:

Hello, Wiliam
Hello, Wiliam
Hello, Christina
  • The first output is easy to understand. In setTimeout’s callback function, this uses the default binding, while in non-strict mode, it executes the global object.
  • Is the second output a bit confusing? When you say XXX.fun (), this in fun refers to XXX. Why not this time? Why?

    In fact, we can understand it this way: setTimeout(fn,delay){ fn (); , which is equivalent to assigning person2.sayHi to a variable and finally executing the variable. At this time, this in sayHi obviously has nothing to do with person2.

  • The third is also in the callback of setTimeout, but we can see that this is person2.sayHi () is using implicit binding, so this is this pointing to person2 and has nothing to do with the current scope.

By reading this, you may be a little tired, but promise me, don’t give up, ok? Hold on a little longer and you will be able to master this knowledge point.

Explicit binding

Explicit binding is better understood, that is, the object pointed to by this is explicitly specified by means of call,apply,bind bind. (Note: bind is explained separately as a hard binding in Javascript you don’t know)

The first parameters of call,apply, and bind are the objects pointed to by this of the corresponding function. Call has the same function as apply, except that the method of parameter transmission is different. Both call and apply execute the corresponding functions, while the bind method does not.

function sayHi(){
    console.log('Hello,', this.name);
}
var person = {
    name: 'YvetteLau',
    sayHi: sayHi
}
var name = 'Wiliam';
var Hi = person.sayHi;
Hi.call(person); //Hi.apply(person)

The output is Hello, YvetteLau. because this is explicitly bound to person using hard binding.

So, does the use of hard binding mean that there will be no binding loss encountered by implicit binding? Obviously this is not the case, do not believe it, continue to look down.

function sayHi(){
    console.log('Hello,', this.name);
}
var person = {
    name: 'YvetteLau',
    sayHi: sayHi
}
var name = 'Wiliam';
var Hi = function(fn) {
    fn();
}
Hi.call(person, person.sayHi); 

The output is Hello, Wiliam. The reason is very simple. Hi.call(person, person.sayHi) does bind this to this in Hi. However, when fn is executed, it is equivalent to directly calling the sayHi method (remember: person.sayHi has been assigned to fn, and the implicit binding has been lost). the value of this is not specified, but the default binding.

Now, we hope that the binding will not be lost. What should we do? It is very simple. When fn is called, it is also hard bound.

function sayHi(){
    console.log('Hello,', this.name);
}
var person = {
    name: 'YvetteLau',
    sayHi: sayHi
}
var name = 'Wiliam';
var Hi = function(fn) {
    fn.call(this);
}
Hi.call(person, person.sayHi);

At tHis time, the output result is Hello, YvetteLau, because person is bound to this in hi function, fn binds this object to sayHi function. At this time, this in sayHi points to the person object.

So far, the revolution has almost won. Let’s look at the last binding rule: new binding.

New binding

JavaScript is different from C++,and there is no class. In javaScript, constructors are only functions called when using the new operator. These functions are no different from ordinary functions. They do not belong to a certain class and cannot instantiate a class. Any function can be called using new, so there is no constructor, only “constructor call” to the function.

Calling a function with new automatically performs the following actions:

  1. Create a new object
  2. Assigns the scope of the constructor to the new object, that is, this points to the new object
  3. Executes the code in the constructor
  4. Returns a new object

Therefore, when we use new to call a function, a new object is bound to this of the function.

function sayHi(name){
    this.name = name;
    
}
var Hi = new sayHi('Yevtte');
console.log('Hello,', Hi.name);

The output is Hello, Yevtte, because at varhi = newsayhi (‘yevte’); This step binds this in sayHi to the Hi object.

Binding priority

We know that this has four binding rules, but what if multiple rules are applied at the same time?

Obviously, we need to know which binding method has higher priority. The priority of these four binding methods is:

New binding > explicit binding > implicit binding > default binding

How did you get this rule? If you are interested, you can write a demo to test it, or just remember the above conclusion.

Binding exception

There are exceptions to everything, and so are the rules of this.

If we pass null or undefined as the binding object of this into call, apply or bind, these values will be ignored when calling, and the default binding rule is actually applied.

var foo = {
    name: 'Selina'
}
var name = 'Chirs';
function bar() {
    console.log(this.name);
}
bar.call(null); //Chirs 

The output result is Chirs, because the default binding rule is actually applied.

Arrow function

Arrow function is newly added in ES6. It has some differences from ordinary functions. Arrow function does not have its own this. Its this inherits this from the outer code library. When using the arrow function, the following points should be paid attention to:

(1) this object in the function body inherits this of the outer code block.

(2) It cannot be used as a constructor, that is, the new command cannot be used, otherwise an error will be thrown.

(3) arguments object cannot be used, which does not exist in the function body. If you want to use, you can use rest parameters instead.

(4) The yield command cannot be used, so the arrow function cannot be used as the Generator function.

(5) The arrow function does not have its own this, so you cannot use call (), apply (), bind () to change the direction of this.

OK, let’s take a look at this of the arrow function.

var obj = {
    hi: function(){
        console.log(this);
        return ()=>{
            console.log(this);
        }
    },
    sayHi: function(){
        return function() {
            console.log(this);
            return ()=>{
                console.log(this);
            }
        }
    },
    say: ()=>{
        console.log(this);
    }
}
let hi = obj.hi();  //输出obj对象
hi();               //输出obj对象
let sayHi = obj.sayHi();
let fun1 = sayHi(); //输出window
fun1();             //输出window
obj.say();          //输出window

So why is this? If you say this in the arrow function is the object at the time of definition, the result shows that it is not what you expected. According to this definition, this in Say should be obj.

Let’s analyze the above implementation results:

  1. obj.hi(); According to the implicit binding rule of this, this is bound to obj, so the output obj is well understood.
  2. hi(); This step is performed by the arrow function, which inherits this from the previous code base. We have just concluded that this at the next level is obj, and obviously this here is obj.
  3. Execute sayHi (); This step is also very easy to understand. We said earlier that this kind of implicit binding is lost. This is the default binding, and this refers to the global object window.
  4. fun1(); This step is to execute the arrow function. If according to the previous understanding, this refers to the object where the arrow function is defined, then this obviously doesn’t make sense. OK, this according to the arrow function is this inherited from the outer code library, which is very easy to understand. We have just analyzed the outer code base. this points to window, so the output here is window.
  5. obj.say(); The execution is an arrow function. this does not exist in the current code block obj. You can only look up and find the global this, which points to window.

You said this of the arrow function is static?

It is still the previous code. Let’s see if this in the arrow function is really static?

I want to say: no

var obj = {
    hi: function(){
        console.log(this);
        return ()=>{
            console.log(this);
        }
    },
    sayHi: function(){
        return function() {
            console.log(this);
            return ()=>{
                console.log(this);
            }
        }
    },
    say: ()=>{
        console.log(this);
    }
}
let sayHi = obj.sayHi();
let fun1 = sayHi(); //输出window
fun1();             //输出window

let fun2 = sayHi.bind(obj)();//输出obj
fun2();                      //输出obj

It can be seen that fun1 and fun2 correspond to the same arrow function, but the output result of this is different.

Therefore, please keep in mind that the arrow function does not have its own this, and this in the arrow function inherits this from the outer code library.

Summary

This is the end of the rule about this, but it still needs constant training to see the object this is bound to at a glance.

Let’s review the original question.

1. How to judge exactly what this is pointing at?

  1. Whether the function is called in new (new binding), if so, this binds the newly created object.
  2. Whether the function is called through call,apply, or bind (i.e. hard binding), if so, this binds to the specified object.
  3. Whether the function is called in a context object (implicit binding), if so, this binds to that context object. Obj.foo ()
  4. If none of the above is true, the default binding is used. If it is in strict mode, it is bound to undefined, otherwise it is bound to a global object.
  5. If Null or undefined is passed into call, apply or bind as the binding object of this, these values will be ignored when calling, and the default binding rule is actually applied.
  6. If it is an arrow function, this of the arrow function inherits this of the outer code block.

2. Analysis of execution process

var number = 5;
var obj = {
    number: 3,
    fn: (function () {
        var number;
        this.number *= 2;
        number = number * 2;
        number = 3;
        return function () {
            var num = this.number;
            this.number *= 2;
            console.log(num);
            number *= 3;
            console.log(number);
        }
    })()
}
var myFun = obj.fn;
myFun.call(null);
obj.fn();
console.log(window.number);

Let’s analyze the execution process of this code.

1. When defining obj, the closure corresponding to fn is executed, and the function in the closure is returned. When executing the code in the closure, it is obvious that new binding cannot be applied (there is no new keyword), nor does hard binding (there is no call,apply,bind keyword). Is there implicit binding? Obviously not, if there is no XX.fn (), then you can be sure there is no implicit binding applied, so the default binding is applied here, and this is bound to window (browser execution environment) in non-strict mode. [It is easy to be confused here that this refers to obj, so be sure to note that unless it is an arrow function, this and lexical scope are two different things and must be kept in mind]

window.number * = 2; //window.number的值是10(var number定义的全局变量是挂在window上的)

number = number * 2; //number的值是NaN;注意我们这边定义了一个number,但是没有赋值,number的值是undefined;Number(undefined)->NaN

number = 3;  //number的值为3

2.myFun.call(null); As we said earlier, the first parameter of call is passed null, and the default binding is called;

fn: function(){
    var num = this.number;
    this.number *= 2;
    console.log(num);
    number *= 3;
    console.log(number);
}

At the time of execution:

var num = this.number; //num=10; 此时this指向的是window
this.number * = 2;  //window.number = 20
console.log(num);  //输出结果为10
number *= 3;  //number=9; 这个number对应的闭包中的number;闭包中的number的是3
console.log(number);  //输出的结果是9

3.obj.fn(); Implicit binding is applied, and this in fn corresponds to obj.

var num = this.number;//num = 3;此时this指向的是obj
this.number *= 2; //obj.number = 6;
console.log(num); //输出结果为3;
number *= 3; //number=27;这个number对应的闭包中的number;闭包中的number的此时是9
console.log(number);//输出的结果是27

4. the last step console.log(window.number); The output is 20

Therefore, the results in the group are:

10
9
3
27
20

According to the results in strict mode, everyone can analyze and consolidate the knowledge points according to what he learned today.

Finally, congratulations to the friends who insisted on finishing reading. You have successfully got this knowledge point, but if you want to master it completely, you still need to review and practice more. If you have a good this exercise, please leave a message in the comments section. Let’s make progress together!

Thank you for taking the precious time to read this article. If this article gives you some help or inspiration, then don’t be stingy with your praise and Star Ha. Your affirmation is my greatest motivation to move forward.https://github.com/YvetteLau/ …

Thank you for pointing out the following additional reference links:

  • JavaScript Books You Don’t Know
  • ES6 Documentation-Arrow Functions (http://es6.ruanyifeng.com/#do …
  • Classic face-to-face articles, if there is a link, you can leave me a message.

Recommend to pay attention to my public number:

clipboard.png