# Scope Chain and this

JavaScript scope refers to the range of variables that can be accessed. There are three types Global scope, Function scope and Block scope.

A piece of JavaScript code needs to be compiled by the JavaScript engine before it is executed. After the compilation is completed, it will enter the execution phase.

On the basis of these, let's talk about what a scope chain is today.

# scope chain

First let's look at the following code:

function bar() {
    console.log(scope)
}
function foo() {
    var scope = "function"
    bar()
}
var scope = "global"

foo()

What do you think the bar function and foo function print out in this code? This will analyze the execution flow of these two pieces of code.

Regarding scope chaining, many people will find it confusing, but if you understand the concepts of call stack, execution context, lexical environment, variable environment, etc., then you will understand the scope chain very easily.

In fact, in the variable environment of each execution context, an external reference is included to point to the external execution context. We call this external reference outer .

For example, when the above code is looking for the scope variable, if it is not found in the current variable environment, the JavaScript engine will continue to search in the execution context pointed to by outer. We call this chain of lookups the scope chain.

Now you know that variables are searched through the scope chain, but there is still a question that has not been solved, the bar function called by the foo function, then why the external reference of the bar function is the global execution context, not the execution context of the foo function?

To answer this question, you also need to know what lexical scope is . This is because during JavaScript execution, its scope chain is determined by lexical scoping.

# lexical scope

Lexical scope means that the scope is determined by the position of the function declaration in the code, so the lexical scope is a static scope, which can predict how the code looks for the identifier during the execution process.

This may not be easy to understand, let's look at the following picture:

Because the JavaScript scope chain is determined by the lexical scope, the entire order of the lexical scope chain is: foo function scope -> bar function scope -> main function scope -> global scope.

That is to say, the lexical scope is determined at the compilation stage of the code, and has nothing to do with how the function is called.

All in all, the scope chain of the JavaScript language is determined by lexical scoping, which is determined by code structure( or position).

# this

We need to distinguish the scope chain and this are two different systems, and there is not much connection between them.

As can be seen from the figure, this is bound to the execution context , that is to say, there is a this in each execution context.

The execution context is mainly divided into three types - the global execution context, the function execution context and the eval execution context, so the corresponding this is only these three - this in the global execution context, this in the function execution context and this in eval execution context.

# this in the global execution context

You can type console.log(this) in the console to print out this in the global execution context, and the final output is the window object. So we can come to the conclusion that this in the global execution context refers to the window object. This is also the only point of intersection between this and the scope chain.

# this in the function execution context

Let's look at the following code:

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

We print out the this value inside the foo function, execute this code, and the window object is also printed out, which means that when a function is called by default, the this in its execution context also points to the window object.

Can we set this in the execution context to point to other objects? The answer is yes. In general, there are three ways to set the this value in the function execution context.

1. Set by call method of the function

const protagonist = {
    firstName: 'Sherlock',
    lastName: 'Holmes',
    getFullName: function() {
        return this.firstName + ' ' + this.lastName;
    }
};

const story = function(interest, hobby) {
    console.log(this.getFullName() + ' loves ' + interest + ' and ' + hobby);
};

story.call(protagonist, 'reasoning', 'algorithm'); 

// story.apply(protagonist,['reasoning', 'algorithm']); 

// const tellStory = story.bind(protagonist)
// tellStory('reasoning', 'algorithm')

The call() method calls a function with a given this value and arguments provided individually.

2. Set the method by calling the object

To change the this pointer in the function execution context, in addition to the call method of the function, we can also call it through the object, such as the following code:

const protagonist = {
    firstName: 'Sherlock',
    lastName: 'Holmes',
    getFullName: function() {
        console.log(this)
    }
};
protagonist.getFullName()

So, we can come to the conclusion: use the object to call a method inside it, and the this of the method points to the object itself.

Let's do a little change:

const protagonist = {
    firstName: 'Sherlock',
    lastName: 'Holmes',
    getFullName: function() {
        console.log(this)
    }
};
const fn = protagonist.getFullName
fn()

Execute this code, we will find that this again points to the global window object.

So by comparing the above two examples, we can draw the following two conclusions:

  • When calling a function in the global environment, the this inside the function points to the global variable window.
  • Calling a method inside of an object whose this in the execution context of the method points to the object itself.
  1. Set by the constructor

We can set this in the constructor like this, as in the sample code below:

function CreateObj(){
  this.name = "E"
}
const instance = new CreateObj()

In fact, when executing new CreateObj(), the JavaScript engine does the following four things:

  • First create an empty object instance;
  • Then call the CreateObj.call method and use instance as the parameter of the call method, so that when the execution context of CreateObj is created, its this points to the instance object;
  • Then execute the CreateObj function. At this time, the this in the execution context of the CreateObj function points to the instance object;
  • Finally, the instance object is returned.

For better understanding, we can use code to demonstrate:

const instance = {}
CreateObj.call(instance)
return instance

In this way, we have constructed a new object through the new keyword, and this in the constructor is actually the new object itself.

# Summarize

The scope chain and this are two different systems, and there is not much connection between them. The only intersection between this and the scope chain is that, in some cases, they both point to the window object.

The scope chain is determined by lexical scoping, which is determined by code structure( or position). But this is bound to the execution context.

If you feel that this of some cases is not easy to identify, We can use const self = this to convert this system into a scope system.