• notice
  • Congratulations on the launch of the Sought Tech site

Detailed explanation of this in JavaScript


As a front-end developer, when I go to an interview, I am most afraid of the two knowledge points that the interviewer asks, one is the closure and the other is this. These two knowledge points are very important and not easy to understand. The author will explain this in detail to help everyone master this.

Knowledge reserve

what is execution context

When a function runs, an execution environment is created, which is called an execution context, and JavaScript processes them in a stack, which is the function call stack. The function call stack specifies the execution order of JavaScript code. The bottom of the stack is always the global context, and the top of the stack is the currently executing context. When the code encounters the global environment, function environment or eval environment (the eval environment is not recommended) during the execution of the code, an execution context will be generated and placed in the function call stack. Pop the stack automatically.

what is a variable object

As mentioned above, when a function is running, it will create an execution environment, also called an execution context. In the execution context, a special object called a variable object will be created. Basic data types are often stored in variable objects. That is to say, all the variables we declare will be saved to the variable object, in addition, the variable object also includes the following:

  • All arguments in the function (arguments object in Firefox)

  • All function declarations in the current context (functions declared via function)

  • All variable declarations in the current context (variables declared via var)

First of all, we know that when the function is called and executed, a variable object will be generated , and the pointer of this will be determined at this time. So the first thing we need to keep in mind is a very important conclusion: the this of the current function is determined when the function is called and executed . If the current execution context is at the top of the function call stack, then the variable object will become the active object at this time, and the point of this is determined.

this first experience

Let's look at an example first:

var a = 10; var obj = {     a:20 } function fn(){     console.log(this.a) } fn(); // 10 fn.call(obj); // 20

Through the different manifestations of a value, it can be found that this points to window and obj respectively. Let's analyze the specific performance of this step by step.

1. this in the global object

In the global object, this points to itself, which is relatively simple.

// 通过this绑定到全局对象 this.a1 = 10; // 通过声明绑定到变量对象,但是在全局环境中,变量对象就是它本身 var a2 = 20; // 只有赋值操作时,标识符会隐式绑定到全局对象中 a3 = 30; // 输出结果符合预期 console.log(a1); // 10 console.log(a2); // 20 console.log(a3); // 30

2. this in the function

We have already known just now that the different calling methods of fn() cause this to point differently. Therefore, the final pointing result of this has a lot to do with the way the function is called. Here we need to keep in mind the second important conclusion: in the execution context of a function, this is provided by the caller of the function, and the way this is called is determined by the way the function is called . This sentence may be a bit difficult to understand. Let's first look at who the caller is through an example.

function fn(){     console.log(this); } fn();// fn为调用者

If the caller is owned by an object, then when the function is called, the internal this points to the object; if the caller calls independently, the internal this of the function points to undefined. But in non-strict mode, when this points to undefined, it will automatically point to the global object.

Let's take a look:

// 非严格模式下 function fn(){     console.log(this); } fn();// fn是调用者,独立调用,this会指向window window.fn();// fn是调用者,被window所拥有,this会指向window

In non-strict mode, the this of fn() and window.fn() both point to window;

// 严格模式下 function fn(){  'use strict';     console.log(this); } fn(); // fn是调用者,独立调用,this指向undefined window.fn();// fn是调用者,被window所拥有,this指向window对象

After figuring out whether the function is called independently or owned by an object, let's analyze it with some small examples.

// demo1 var a = 10; var obj = {     a:20 } function fn(){     console.log('fn this:', this);          function foo(){         console.log("foo this:", this.a)     }     foo(); } fn(); fn.call(obj);

First of all, let's take a look at the way fn is called, and the pointing of this will be different, but no matter how fn is called, foo is called independently, so the this inside foo points to undefined, because it is in non-strict mode (default), Therefore, it automatically points to the window, and the output of demo1 is as follows:

// fn()执行结果 fn this: Window:{} foo this: 10 // fn.call(obj)执行结果 fn this: Object{a:20} foo this:10

Next, look at the second small example:

// demo2 'use strict'; var a = 10; function foo(){     var a = 1;     var obj = {         a:20,         c: this.a + 20     }     return obj.c; } console.log(window.foo()); console.log(foo());

This code uses strict mode, so when window, foo() is called, the this inside foo points to the window object, and this.a can access the a variable in the global; when the following foo() is called independently, strict In mode, the this inside foo points to undefined, and does not turn to window. At this time, the execution code will report an error. The result of executing the code is as follows

// window.foo() 30 // foo() Uncaught TypeError:Cannot read property 'a' of undefined

Let's look at another example:

// demo3 var a = 10; var foo = {     a:20,     getA:function(){         return this.a;     } } console.log(foo.getA());  // 20 var bar = foo.getA; console.log(bar());  // 10

If you can remember the difference between a caller's independent invocation and being owned by an object, then this topic should be trivial for you.

Let's take a look, in foo.getA(), getA is the caller and is owned by foo, so when getA is executed, this points to foo, and the execution result returns 20; when bar() is executed, bar is the caller, It is called independently. Note here that although the references of bar and foo.getA point to the same function, they are called differently. Therefore, when bar() is executed, this inside getA points to undefined, automatically turns to window, and the result returns 10;

Through the explanation of the above three small examples, is it clear to the direction of this? The following two small topics are given as thinking questions, and analyze the direction of this. Then execute it yourself to verify that your results are correct.

// demo4 function foo(){     console.log(this.a) } function bar(fn){     fn(); } var a = 20; var obj = {     a:10,     getA: foo,     bar: bar } bar(obj.getA); // ? obj.bar(obj.getA); // ?
// demo5 var n = 'window'; var obj = {     n:'object',     getN:function(){         return function(){             return this.a;         }     } } console.log(obj.getN()()); // ?

3.call/apply/bind shows change this pointing

In the actual development process, we do not want to use the default pointing of this. What should we do? JavaScript provides a way to manually set this inside the function, they are call/apply/bind;

Let's look at a small example:

var a = 10; var obj = {     a:20 } function foo(){     console.log(this.a) }

Now there is such a requirement, the expected result is to output 20. If the function foo is called normally, it is obvious that foo is called independently, this points to window, and the output result is 10, which is not as expected. At this time, we can display and change the this pointer inside the function through call/apply/bind

foo.call(obj); // 20 foo.apply(obj); // 20

When a function calls call/apply, the first parameter of call is to determine the point of this inside the function, and the following parameters are the parameters needed when the function is executed, which are passed one by one; the first parameter of apply is the same as that of call , which points to this inside the function, and the parameters required for function execution are passed in the form of an array as the second parameter of apply.

function fn(num1, num2){     return this.a + num1 + num2 } var a = 10; var obj = {     a:20 } // 正常执行 fn(10, 10); // 30 // 通过call改变this指向 fn.call(obj, 10 ,10); // 40 // 通过apply改变this指向 fn.apply(obj, [10, 10]); // 40

Bind and call/apply change the way this points differently. When a function calls call/apply, this changes inside the function, and the function executes immediately. When a function calls bind, the function does not execute immediately, but returns a new function. The parameters of the new function and the this pointer have been bound, and the parameters are the execution parameters of bind.

fn.bind(obj)(10, 10); // 40

In addition, it should be mentioned that there is no this in the arrow function of ES6, which means that the this of the arrow function itself does not work, and its this point is determined by the environment in which the arrow function is located.

4. Reference

1. Decryption of JavaScript core technology

2. The Definitive Guide to JavaScript


Technical otaku

Sought technology together

Related Topic


Leave a Reply