This is the 26th day of my participation in Gwen Challenge

Today, I will use a summary to thoroughly understand this problem in JavaScript and analyze the past life of this.

preface

The first thing to make clear is that this in JS refers neither to the function itself nor to the lexical scope of the function. It’s actually a binding that happens when the function is called, which means that what this refers to depends on how you call the function.

This and this is a concept that many people will confuse. As long as the working principle of this is clear, it will be easy to understand. Let’s see a demo first.

function foo() {
  console.log(this.a)
}

var a = 2
foo()
var obj = {
  a: 2.foo: foo
}
obj.foo()

// In both cases' this' only depends on the object before the function is called, and the second case has precedence over the first case
// The following is the highest priority: 'this' is bound only to' c 'and cannot be referred to by' this' in any way

var c = new foo()
c.a = 3
console.log(c.a)
// Another option is to use call, apply, bind to change this, which is next in priority to newSo that's clear. A lot of codethisI think there should be no problem. Now let's look at the arrow function, rightthis.function a() {
  return () = > {
     return () = > {
       console.log(this)}}}console.log(a()()())
Copy the code

The arrow function does not have this. The this in this function depends only on the this of the first function outside it that is not the arrow function. In this case, because calling a matches the first case in the previous code, this is the window. And once this is bound to the context, it will not be changed by any code.

Objects in This

The This object is bound at runtime based on the function’s execution environment: in a global function, This equals window, and when the function is called as a method of an object, This equals that object. However, the execution environment of an anonymous function is global, so its this object usually points to window. But sometimes this may not be obvious because of the way closures are written.

var name = "The Window";
var object = {
  name : "My Object".getNameFunc : function(){
    return function(){
      return this.name; }; }}; alert(object.getNameFunc()());// "The Window" (in non-strict mode)
Copy the code

The above code creates a global variable name and then an object containing the name attribute. This object also contains a method — getNameFunc(), which returns an anonymous function that returns this.name. Since getNameFunc() returns a function, calling Object.getNameFunc ()() immediately calls the function it returns, which returns a string. However, The string returned in this example is “The Window”, The value of The global name variable.

Each function automatically gets two special variables when called: this and arguments. The inner function searches for these two variables only up to its live object, so it is never possible to directly access the two variables in the outer function. However, you can give the closure access to this object by storing it in a variable accessible to the closure, as shown below.

var name = "The Window";
var object = {
  name : "My Object".getNameFunc : function(){ 
    var that = this;
    return function(){
      returnthat.name; }; }}; alert(object.getNameFunc()());// "My Object" 
Copy the code

Before defining the anonymous function, we assign this to a variable named that. After the closure is defined, the closure can also access this variable, because it is a variable we specifically name in the inclusion function. Even after the function returns, that still refers to object, so calling Object.getNamefunc ()() returns “My object “. The same problem exists with this and arguments. If you want to access the Arguments object in scope, you must save a reference to that object in a variable accessible to another closure. In several special cases, the value of this can change unexpectedly. For example, the following code is the result of modifying the previous example.

var name = "The Window";
var object = {
  name : "My Object".getName: function(){
    return this.name; }};Copy the code

The getName() method here simply returns the value of this.name. Here are several ways to call Object.getName () and their results.

object.getName(); //"My Object"
(object.getName)(); //"My Object"
(object.getName = object.getName)(); 
// "The Window", in non-strict mode
Copy the code

The first line of code calls Object.getName () as usual, returning “My object “because this.name is Object.name. The second line of code parentheses the method before calling it. GetName (Object.getName) is defined identically, although the parentheses make it look like it’s just referring to a function. The third line of code executes an assignment statement and then calls the result of the assignment. Since The value of this assignment expression is The function itself, The value of this cannot be maintained and “The Window” is returned.

Execution context

When JS code is executed, there are three execution contexts

Global execution context
Function execution context
Eval Execution context

Each execution context has three important attribute variable objects (VO), which contain variables, function declarations, and function parameters. This attribute can only be accessed in the scope chain in the global context (JS is lexically scoped, meaning that the scope of a variable is determined at definition time).

// this
var a = 10;
function foo(i) {
  var b = 20; } foo()Copy the code

For the above code, there are two contexts in the execution stack: the global context and the function Foo context.

stack = [
  globalContext,
  fooContext
]
Copy the code

For global context, VO looks something like this.

globalContext.VO === globe;
globalContext.VO = {
  a: undefined.foo: <Function>,}Copy the code

For foo, VO cannot access only the active object (AO).

fooContext.VO === foo.AO
  fooContext.AO {
    i: undefined.b: undefined.arguments: <>} // Arguments is an object that is unique to the function (arrow functions don't have it) // This object is a pseudo-array that has the 'length' attribute and can be accessed via subscripts // The 'callee' attribute in this object represents the function itself // the 'caller' attribute represents the caller of the functionCopy the code

A Scope chain can be thought of as a list of its own variable objects and its parent variable objects, looking up the parent variable through the [[Scope]] property.

fooContext.[[Scope]] = [ globalContext.VO ] fooContext.Scope = fooContext.[[Scope]] + fooContext.VO fooContext.Scope = [ Foocontext.vo, globalContext.vo] Let's look at a cliche example,var

b();            // call b
console.log(a); // undefined

var a = 'Hello world';
function b() {
  console.log('call b')}Copy the code

The above output must have been clear to all of you, because functions and variables are promoted. The usual explanation for promotion is to move the declared code to the top, but there’s nothing wrong with that and it’s easy to understand. But a more accurate explanation would be that there are two phases to generating the execution context. The first stage is to create the stage (specific steps is to create the VO), JS interpreter will find out the need to increase variables and functions, and give them open up well in the memory space in advance, function words will the entire function in the memory, variable declaration and assignment is undefined, so in the second phase, namely code execution phase, We can just use it ahead of time. In promotion, the same function overrides the previous one, and the function takes precedence over the variable promotion.

b() // call b second

function b() {
  console.log('call b fist')}function b() {
  console.log('call b second')}var b = 'Hello world'
Copy the code

Var can cause a lot of errors, so let was introduced in ES6. A let cannot be used before a declaration, but this is not to say that a let cannot be promoted. A let promotes the declaration but does not assign a value because a temporary dead zone causes it to not be used before the declaration. There are a few things to be aware of for non-anonymous instant-execution functions

var foo = 1
(function foo() {
 foo = 10
 console.log(foo)
}()) ƒ foo() {foo = 10; console.log(foo) }
Copy the code

Because when JS interpreter when meeting the anonymous immediate execution function, will create a helper for specific objects, and then the function name as the attribute of the object, so the function can access to the internal foo, but this value is read-only, so the value assigned to it is not effective, so the results of the print or the function, And the external values have not changed.

// 
specialObject = {};
  Scope = specialObject + Scope;
  foo = new FunctionExpression;
  foo.[[Scope]] = Scope;
  specialObject.foo = foo; // {DontDelete}, {ReadOnly}
  delete Scope[0]; // remove specialObject from the front of scope chain
Copy the code

The principle of this

This in JavaScript is a reference to the object being called. Figuratively speaking, a reference to the object before the “.”. In JavaScript, there are functions (types) and functions (objects), but they are always bound to an object when called. Calling a function that doesn’t appear to be bound to any object is actually bound to a “global” object. In the browser DOM this global object is the window. When the new operator is used to construct a new object, the “this” in the constructor following new refers to an empty new object constructed from new. There is nothing in this object yet (DontEnum can’t see it), and an assignment of the form this. XXX in the constructor creates new properties for that object. After all, JavaScript “objects” are nothing more than associative arrays. As explained in the Concepts of Programming Languages book, objects in JavaScript are very similar to Hash in Perl. The popular JSON has been developed to take advantage of this feature.

 function fooConstructor() {  
        this.variable = 1;  
    }  

    function makeAnonymousFunction() {  
        return function() {  
            this.gooValue = 2;  
        };  
    }  

    fooConstructor();          // invoke a function that looks like a constructor  
    makeAnonymousFunction()(); // invoke an anonymous function  
    document.write(variable + "<br />");  
    document.write(window.gooValue + "<br />");  

    var obj = new fooConstructor(); // invoke a constructor with "new"  
    document.write(obj.variable + "<br />");  

// Run the result
1  
2  
1  
Copy the code

Although fooConstructor() is not called as “object.method()” when we first call it, it is actually equivalent to window.fooconstructor (). We then implicitly pass the window object (the “global” object in the browser DOM) to the called function. In fooConstructor this points to the window and creates the variable property for the window object, giving it a value of 1. The makeAnonymousFunction()() call is made to demonstrate the independence of this reference to the nested hierarchy. MakeAnonymousFunction () returns a function object, but instead of giving it a name, we call it directly. As in the previous example, this call creates a property called gooValue for the window object and assigns it a value of 2. We then demonstrated how to create a new object with the new operator. It’s a very common one and there’s nothing to explain.

conclusion

Simply put, this in JavaScript is a reference to the object being called. Figuratively speaking, a reference to the object before the “.”.

reference

JavaScript advanced programming