To get started, let’s look at two pieces of code:

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

function bar() {
    var a = 3;
    foo();
}

var a = 2;
bar();
Copy the code
var foo = 1;
function bar() {
    if(! foo) {var foo = 10;
    }
    console.log(foo);
}
bar();
Copy the code
  1. If you can’t find a variable in a function scope, you can go up the scope chain. Why do we have var a = 3; Does foo() refer to global a=2?

  2. Why foo is 10? Is var foo = 10 implemented?

In view of this phenomenon in the code, the author also looked for various posts and articles before, when looking at these articles, the terms scope, lexical environment, static scope, execution context and so on kept appearing. The scope of a function is determined by its lexical context at the time it is created. The scope of a function is determined at runtime, not at creation time. How the hell did that decide)… And so on, plus prototype chains, scope chains, closures, anonymous functions, immediate functions… What the hell! I believe that all of you have similar experiences. You have heard and understood these concepts, but they are vague.

In order to fully understand what I was writing code to do, I decided to take a look, spend some time exploring, and now I think I know the truth, I can share what I know. Not to do anything fancy, but to get a better understanding of the nature of speech.

Note: This series of articles is based on ECMAScript 5.1(ES5), so if you’re wondering, “Why is your runtime environment different from what I see? I see variable objects (ES3) and realm(ES6).” For ES6, I will publish a series just to talk about it. Most of this series deals only with ES5.

Household Registration System

Before we get started, let’s ask a question, okay? Where does the JS engine find the value of a variable to reference when executing code, and how does it find the function to execute when calling a function? The answer is surprisingly simple: register first, use later. It’s like when you go to school, you register first, your name, gender, etc. Registration finished, class, the teacher shouted “xiao Ming”, xiao Ming can respond to “um, brother here”, is not. For example, if there is no registration of Xiao Hong “this”, there is no xiao Hong in the registration book, so the teacher can not call it (ReferenceError “not defined”). But if “xiao Li” school registration, the table also has its name “Xiao Li”, but this day Xiao Li played electric truant, the teacher shouted “Xiao Li”, no one should (default value undefined, here small partner to make clear, undefined is not to say that there is no definition, but to say that this variable is defined, its value is undefined, Undefined is a value in JS, remember? .

When school starts, students look for teachers to register for enrollment, so we JS variable name, function name to find who to register? Check in for Lexical Environments!

Lexical Environments

Lexical Environments, it’s called Lexical Environments because it corresponds to the structure of the source code, of the words that you write in the source code, and the context is set when you write the code. Lexical Environments correspond to four types of code structures:

  • Global code: a lexical environment for source code
  • Function code: A function block itself is a new lexical environment
  • Eval: Code that enters an eval call sometimes creates a new lexical environment
  • With: A with block is also its own lexical environment
  • Catch construction: a catch construction is also its own word environment

Why is that? I don’t know, that’s how ES5 is designed…

“No, no, no, I remember that only global code, function code, and eval code can create run context. You have five.”

Well, you’re right that the running context is created only for global code, function code, and eval code, but I’m talking about Lexical Environments here. Not the run context.

It looks something like this:

This is a bit like a school. The head teacher is responsible for registering the students of each class, the head teacher is responsible for registering the teachers of the year, and the principal is responsible for registering other administrative staff of the school.

The lexical context of the with and catch constructs is only used for retrieval, not registration. The var and function declarations of the with and catch constructs are also used for other (where?) functions. The registry is bound to the lexical environment, which is actually a registry search agent (agent, may not have a registry to return to you have this person. These details will be explained in more detail later.

Looking at the first chart above, there are a few things to note:

  1. The lexical context for the four structures is drawn for demonstration purposes, not to create the four lexical contexts all at once. Remember, the lexical context is created before entering the above code structures, just as the student registration process is done before school starts.
  2. From the diagram, you can see that the lexical environment is nested, so how to implement nesting? This requires understanding the structure of the lexical environment.

The teacher registers the student information in the register. Where should I register the variable information? Ha ha, Lexical Environments has its own registry -Environment Records. Let’s take a look at the Lexical Environments for a second:

The lexical environment consists of two parts:

  • Environment Records: This is where variables are registered
  • Outer: Outer is the outer lexical environment that points to, and contains (surrounds) the local lexical environment

The pseudo-code representation would look something like this:

function LexicalEnvironment() {
    this.EnvironmentRecord = undefined;
    this.outer = undefined; //outer Environment Reference
}
Copy the code

The outer is important because it is the key to keeping the chain of scopes together. The teacher in charge has a register and a long phone number for the year. The outer is a long phone number. What does it do? I’m looking for someone named 9527. I can’t find him on my list. Maybe he’s not a student, but a teacher. . Has on its own register for long, if you cannot find, long also have a phone number to the headmaster’s phone number, then long call the principal, “the headmaster, I’m looking for a 9527 people, I can’t find the register, can is a school administrative staff, you can register on you look for this man?”. When I reached the principal, his phone number was null. If I couldn’t find it, I couldn’t find it.

Outer is, in a word, a reference to external Lexical Environments.

In our JS, global Lexical Environments is the principal, and it’s also called GlobalEnvironment because GlobalEnvironment is the outermost Lexical environment, So GlobalEnvironment outer = null. And the outer of the Lexical Environments of the functions defined in the code associated with the GlobalEnvironment is GlobalEnvironment, with and catch, and again, we’ll talk more about how they’re related.

Now look at the “register” -EnvironmentRecord. EnvironmentRecord is the place to register variable information. Environmentrecords in ES5 fall into two categories, just as some teachers record information in a book and some in a computer:

  • Declarative Environment Records is mainly used in function and catch lexical environments
  • Object environment records. Mainly used for lexical environments with and global

Declarative Environment Records can be simply understood as dictionary type structure, key-value form conclusion variables and other corresponding names and values.

Object Environment Records will be associated with an object, and the attribute value of this object will be used to register the corresponding name and value of variables.

Pseudo code (these codes are only used to illustrate the related structure, do not guarantee rigor and feasibility, pseudo code!) :

function EnvironmentRecord(obj) {

    if(isObject(obj)) {
        this.bindings = object;
        this.type = 'Object';
    }
    this.bindings = new Map();
    this.type = 'Declarative';
}


EnvironmentRecord.prototype.register = function(name) {
    if (this.type === 'Declarative')
        this.bindings.set(name,undefined)
    this.bindings[name] = undefined;
}

EnvironmentRecord.prototype.initialize = function(name,value) {
      if (this.type === 'Declarative')
        this.bindings.set(name,value);
    this.bindings[name] = value;
}

EnvironmentRecord.prototype.getValue = function(name) {
    if (this.type === 'Declarative')
        return this.bindings.get(name);
    return this.bindings[name];
}
Copy the code

Global Environment

GlobalEnvironment, which is a special Lexical environment, outer is null, Its EnvironmentRecord is an object EnvironmentRecord associated with the global object (browser: Window object). It looks something like this.

var GlobalEnvironment = new LexicalEnvironment();
GlobalEnvironment.outer = null;
GlobalEnvironment.EnvironmentRecord = new EnvironmentRecord(globalobject); ;// GlobalObject can be seen as a window in the browser environment
Copy the code

Lexical Environments, mentioned above, are used to register variable and related function names, and it is known that this name is registered on EnvironmentRecord for Lexical Environments.

Which begs the question, when and how to register? Should I register directly with teacher Environments, or should I set up an office with a registration window to provide registration services?

We will explain in detail in the next article!

conclusion

//Lexical Environment
function LexicalEnvironment(a) {
    this.EnvironmentRecord = undefined;
    this.outer = undefined; //outer Environment Reference
}

//EnvironmentRecord
function EnvironmentRecord(obj) {

    if(isObject(obj)) {
        this.bindings = object;
        this.type = 'Object';
    }
    this.bindings = new Map();
    this.type = 'Declarative';
}

EnvironmentRecord.prototype.register = function(name) {
    if (this.type === 'Declarative')
        this.bindings.set(name,undefined);
    this.bindings[name] = undefined;
}

EnvironmentRecord.prototype.initialize = function(name,value) {
      if (this.type === 'Declarative')
        this.bindings.set(name,value);
    this.bindings[name] = value;
}

EnvironmentRecord.prototype.getValue = function(name) {
    if (this.type === 'Declarative')
        return this.bindings.get(name);
    return this.bindings[name];
}

// GlobalEnvironment

function creatGlobalEnvironment(globalobject) {
	var globalEnvironment = new LexicalEnvironment();
	globalEnvironment.outer = null;
	globalEnvironment.EnvironmentRecord = new EnvironmentRecord(globalobject);
	returnGlobalEnvironment. } GlobalEnvironment = creatGlobalEnvironment(globalobject)// Can be considered as window in the browser environment

Copy the code