Today we will lead you to learn about scopes in JS, starting with some definitions of scopes.A scope in JS is the accessible range of a variable. This scope can be global or local. In JS, scope is defined by a function. This means that the visibility of variables is based on where they are declared.
In JavaScript, there are the following types of scopes:
Global Scope: Variables that are accessible throughout the JS code are called global variables. Global variables are declared outside all functions and can be referenced anywhere.
Function Scope: Variables declared inside a function can only be accessed from within the function. This means that they are not visible outside the function.
Block Scope: Prior to ES6, JavaScript did not have block-level scopes. However, scopes can be created in specific blocks by using thelet
andconst
keywords.
Understanding the concept of scoping is crucial to writing JavaScript code, as it determines the visibility and lifecycle of variables.
Steps before code execution
Before each piece of code can be executed to produce a result, the following steps are performed, that is, lexical analysis, parsing, and code generation
var a = 3;
1. Lexical analysis
Lexical units: var, a, = ,3
2. Parsing (grammatical analysis)
Converting lexical units into a hierarchically nested tree of program syntax structures — abstract syntax tree
3. Generate code
var a = 3;
valid identifier
In JavaScript, a valid identifier is a name that is used to name a variable, function, object property, and so on.
function foo(a){
var b = 2;
function bar(c){
console.log(a+b+c);
}
bar(3);
}
foo(1);
In the above code a,b,c,bar are valid identifiers for foo,bar is a function declaration where only c is a valid identifier in bar.
Inner scopes can access outer scopes, outer scopes cannot access inner scopes.
function foo(){
var a = 1;
}
foo();
console.log(a);
This code will result in an error, showing that a is not defined, because the code that prints a is in the global scope, while the value of a is defined in the function scope, and the outer scope cannot access the value of a in the inner scope.
var a = 1;
function foo(){
console.log(a);
}
foo();
Change the code, the variable a defined in the global scope, then you can print out the result of a 1, because when you call the function foo, the function to print the value of a, it will be the first in the function foo to find there is no definition of the value of a, if you can not find the value of a function in the scope of the value of a, will go to the global scope to find a value.
Now given a piece of code, do you know what will be printed at the end?
var a = 1;
function foo(){
var a = 2;
console.log(a);
}
foo();
The call stack of this code is shown in the above figure, printing the value of a will first look for it in foo, and it found a=2, so it will finally print out the result as 2. If it is not found in foo, only then will it go to the global look for it.
Statement Elevation
Declaration elevation in JavaScript means that during code execution, variable and function declarations are elevated to the top of their scope, while the actual assignments are left in their original position. This means that variables or functions can be accessed before they are declared, but values accessed before they are assigned will be undefined.
console.log(a);//undefined
var a = 2;
Normally, the result of the above code should report an error, because, the code is executed from top to bottom, there is no definition of a before printing a. However, the result of the execution of this code is undefined
, that is, because the variable declared by var will be declared to be elevated, this code is equivalent to the following code.
var a;
console.log(a);//输出undefined
a = 1;
block scope (computing)
Block-level scoping means that a variable or function declared inside a block of code (usually enclosed in curly braces {}) is visible only inside the block and is not accessible from outside. In JavaScript, block-level scoping is usually used in conjunction with the let and const keywords.
if(true){
let a = 1;//or const a = 1;
}
console.log(a);
The result of the above code is a is not defined
,because let and {} form a block level scope, the outer global scope can not access the inner block level scope, so the result is undefined.
The difference between let and var
- var exists for declaration lifting, let does not exist.
- let will form a block scope with {}.
var can be declared repeatedly, let cannot.
let Temporal Dead Zone, Temporal Dead Zone (TDZ) is a block-level scope where a variable declared using let or const cannot be accessed until it is declared or an error is thrown.
An example:
if(1){
console.log(a);//ReferenceError: Cannot access 'a' before initialization
let a = 2;
}
Accessing a before it is declared throws a ReferenceError, even though a has been assigned the value 2 later, because the variable declared in let is in a temporary dead zone and can’t be accessed until code execution reaches the point of declaration.
const is used to declare immutable constants, while let is used to declare mutable variables. const declared variables are not allowed to be changed.
Spoofing lexical scopes
eval() turns code that doesn’t belong here into something that looks like it was born here.
function foo(str,a){
eval(str);
console.log(a,b);
}
var b = 2;
foo('var b = 3',1);
Under normal circumstances, the result should be 1 and 2, but the result of this code is 1 and 3, because eval transforms the string 'var b = 3'
directly into the code var b = 3
, which is said to be eval() deceiving the lexical scope.
with(){} is used to modify the value of a property in an object, but if the modified property does not exist in the original object, then the property is leaked to the global
function foo(obj){
with(obj){
a = 2;
}
}
var o2 ={
b: 3
}
foo(o2);
console.log(a);
As mentioned earlier, outer scopes cannot access inner scopes, so the above code should conventionally result in a is not defined
, but the above result can be printed out as 2, because with(){} leaks a = 2
to the global.