As mentioned in the previous article, only by understanding the execution context, can we better understand the JavaScript language itself, such as variable promotion, scope, closure, etc. This article will take a look at the scope of JavaScript.
This article is more appropriately called Notes and is derived from What You Don’t Know about JavaScript (Volume 1), Part 1 of scopes and closures. It’s a good story. It’s worth reading.
What is scope
A scope is a set of rules for finding variables by name.
Understanding scope
To understand some basic concepts:
- Engine: Responsible for compiling and executing JavaScript programs from start to finish.
- Compiler: Responsible for parsing and code generation. You can also look at how the JavaScript code is executed, right
- Scope: Is responsible for collecting and maintaining a series of queries made up of all declared identifiers (variables) and enforcing a very strict set of rules that determine access to these identifiers by currently executing code.
Let’s see how the following code executes:
var a = 2;
Copy the code
- Meet Var A,The compilerWill askscopevariable
a
Exists in the same scoped collection. If so, the compiler ignores the declaration and continues compiling; Otherwise, the scope is asked to declare a new variable in the current scope collection nameda
- The followingThe compilerforengineGenerate the code needed at runtime to handle the assignment of a = 2. The engine starts by asking the scope if there are any variables in the current scope set
a
. If so, the engine uses that variable; If not, the engine continues to look for the variable - If the engine finds the a variable, it assigns 2 to it, otherwise it throws an error.
Conclusion: Assignment to a variable performs two actions. First, the compiler declares a variable in the current scope. Then, at runtime, the engine looks for the variable in scope and assigns a value to it if it can find it.
The compiler generates code in the second step of the compilation process, and when the engine executes it, it looks for the variable A to determine if it has been declared. The scope assists in the lookup process, but how the engine performs the lookup affects the final result.
In our case, the engine does an LHS query for variable A, and another type of lookup is called RHS.” L “and “R” represent the left and right side of an assignment operation, respectively. An LHS query is performed when a variable appears on the left side of an assignment operation and an RHS query is performed when a variable appears on the right.
LHS: Trying to find the variable’s container itself so that it can be assigned a value; RHS: Simply look up the value of a variable.
console.log(a);
Copy the code
The reference to A is an RHS reference, because there is no value assigned to the task by A, so you need to look up and get the value of A so that it can be passed to console.log(…).
a = 2;
Copy the code
The reference to a here is an LHS reference, because we don’t really care what the current value is, just want to find the target for the = 2 assignment.
funciton foo(a) {
console.log(a)
}
foo(2);
Copy the code
- The last line of call to foo requires an RHS reference to foo, find the value of foo, and give it to me
- You might easily overlook the implicit a = 2 operation in the code, which occurs when 2 is passed as an argument to
foo
Function,2
Will be assigned to parametersa
, in order to give parametersa
Assign values (implicitly) and need to do so onceLHS
The query. - There is also an RHS reference to A and the resulting value is passed
console.log(...)
.console.log(...)
It also requires a reference to execute itself, so the console object is calledRHS
Query and check if one of the resulting values is calledlog
Methods.
The engine throws ReferenceError when the RHS query cannot find the desired variable in all nested scopes. An RHS query finds a variable, but if you try to manipulate the value of the variable improperly, such as trying to call a value of a non-function type that references an attribute in a value of type null or undefined, the engine will raise a TypeError exception of another type. If the variable is not found when the engine executes an LHS query, it creates one in the global scope. But in strict mode, instead of automatically creating a global variable, ReferenceError is thrown
Add several common error types of JS
The summary is as follows:
A scope is a set of rules for determining where and how to find a variable. If the purpose of the lookup is to assign a value to a variable, an LHS query is used; If the goal is to get the value of a variable, RHS queries are used; The JavaScript engine compiles the code before executing it, and declarations like var a = 2 are broken down into two separate steps
- Var A declares variables in its scope, which is done at the very beginning, before code execution
- Next, variable A = 2 is queried (LHS query) and assigned to it.
Lexical scope
The lexical scope is determined by where you write variables when you write code. The lexing phase of compilation basically knows where and how global identifiers are declared, and thus can predict if they will be looked up during execution.
There are ways to cheat lexical scopes, such as eval and with, which are now prohibited, 1 because strict mode behaves differently than non-strict mode and 2 because there are performance issues, JavaScript engines do a lot of performance tuning at compile time, Many of these optimizations rely on being able to statically analyze the code lexicographically and pre-determine where all variables and functions are defined in order to quickly find identifiers during execution. Eval and with change the scope, so the engine can’t optimize against them.
Global scope and function scope
Global scope
- Variables defined outside and in the outermost function have global scope
var a = 1;
function foo() {}Copy the code
Both the variable A and the function declaration foo are in global scope.
- All variables that do not have a direct assignment defined are automatically declared to have global scope
var a = 1;
function foo() {
b = 2;
}
foo();
console.log(b); / / 2
Copy the code
- All properties of window objects have global scope
Function scope
Function scope means that all variables declared inside a function are always visible inside the function body. An external scope cannot access anything inside a function.
function foo() {
var a = 1;
console.log(a); / / 1
}
foo();
console.log(a); // ReferenceError: a is not defined
Copy the code
Only function {} is scoped, object {} and if(){} are not scoped;
Variable ascension
Promotion means that the declaration is considered to exist within the full scope of the scope in which it appears.
The JavaScript compilation phase is about finding all declarations and associating them with the appropriate scope (the lexical scope core), so all declarations including variables and functions are processed first before any code is executed.
Each scope is promoted.
function foo() {
var a;
console.log(a); // undefined
a = 2;
}
foo();
Copy the code
Note that function declarations are promoted, but function expressions are not.
The block-level scope and variable promotion are covered in detail in the article on understanding var, let, and const from the bottom of JS.
Block-level scope
Let’s look at the following code
for(var i = 0; i < 5; i++) {
setTimeout((a)= > {
console.log(i); })}console.log(The current I is${i}`); // The current I is 5
Copy the code
This code is supposed to print 0,1, 2, 3, 4, but it actually prints 5, 5, 5, 5, 5. We define the variable I directly in the header of the for loop, usually because we only want to use I in the context of the inside of the for loop, but in reality I is bound in the outer scope (function or global).
Block-level scope means that it is not accessible outside the specified block-level scope. There was no concept of block-level scope prior to ES6, which introduced let and const. We can rewrite the above code to make it work the way we want it to.
for(let i = 0; i < 5; i++) {
setTimeout((a)= > {
console.log(i); })}// 0 1 2 3 4
console.log(The current I is${i}`); // ReferenceError: i is not defined
Copy the code
At this point, the for header let not only binds I to the iteration of the for loop, it actually rebinds it to every iteration of the loop, ensuring that it is reassigned using the value at the end of the last iteration of the loop.
The let declaration is attached to a new scope rather than the current function scope (and not to the global scope). But the behavior is the same, summed up as: any variable declared in a scope will be attached to that scope. Const can also be used to create block-level scoped variables, but with fixed values.
The scope chain
JavaScript is a lexical scoped language, where the scope of a variable is known by the location of its definition. Global variables are always defined in a program. A local variable is always defined in the body of the function in which it is declared and in the nested function.
Each piece of JavaScript code has a scope chain associated with it. The scope chain is a list or linked list of objects. When JavaScript needs to look for variable x (a process called variable parsing), it starts with the first variable in the chain. If this object still doesn’t have a property named x, it moves on to the next object on the chain. If the second object still doesn’t have a property named x, JavaScript moves on to the next object, and so on. If none of the objects in the scope chain contains the attribute X, the code is considered to have no X in the scope chain, and eventually a Reference Error exception is thrown.
There are three nested scopes in the following scope.
function foo(a) {
var b = a * 2;
function bar(c) {
console.log(a, b, c)
}
bar( b * 3);
}
foo(2);
Copy the code
Bubble 1 contains the entire global scope with a single identifier: foo; Bubble 2 contains the scope created by Foo, with three identifiers: A, bar, and b; Bubble 3 contains the scope created by bar with a single identifier: C
Perform the console. The log (…). And find references to variables A, b, and c. Let’s look at the process of finding these variables. It starts with the innermost scope, bar(..). The function’s scope bubble starts looking, and the engine can’t find a here, so it goes up one level to the nested foo(…). Continue the search in the scope of A is found here, so this reference is used. The same is true for B, while for C, the engine is in bar(..) I found it in.
If a and C both exist in bar(…) Inside, the console. The log (…). You can use bar(…) directly. In, without having to go outside to foo(..) In the search. The scope stops looking for the first matched identifier.
You can define identifiers of the same name in multiple nested scopes. This is called the “masking effect”.
var a = 'External A'.;
function foo() {
var a = 'A' inside foo;
console.log(a); // a inside foo
}
foo();
Copy the code
Scope and execution context
The execution of JavaScript is divided into two stages: interpretation and execution
Explain the stage
- Lexical analysis
- Syntax analysis
- Scope rules are determined
Execution phase
- Create execution context
- Execute function code
- The garbage collection
The scope is defined when the function is defined, not when the function is called, but the execution context is created before the function is executed.
conclusion
- A scope is a set of rules for determining where to look and how to find a variable.
- The lexical scope is determined when you write code. JavaScript is a lexical scoped language, where the scope of a variable is known by the location of its definition. The let and const declared variables introduced in ES6 are in block-level scope.
- Declaration promotion means that a declaration is considered to exist within the entire scope in which it appears.
- When looking for a variable, it starts with the internal scope. If it doesn’t find one, it goes up one level, and so on.
- The scope is defined when the function is defined, and the execution context is created before the function is executed.
reference
- In-depth understanding of JavaScript scopes and scope chains
- An in-depth understanding of javascript archetypes and closure families
- Scope and lexical scope
- JavaScript You Don’t Know (Volume 1)
other
Recently, WE launched a 100-day front-end advanced plan, which is mainly to dig into the principle behind each knowledge point. Welcome to follow the wechat public account “Mucode star”, and we will study together and punch the clock for 100 days. At the same time, we will also share some of our own learning experience and ideas, welcome to communicate with you.