Chen Chen, a programmer of “Life is at rest”, is the front group of The First Profit Center of Wedoctor.
When you write JavaScript,
How often do you see this error * is not defined?
Does undefined always appear?
This is because the access to the variable is invalid or unavailable, and it is the scope of the variable that defines the code’s availability. So what is scope?
scope
A basic programming language is the ability to store a value in a variable and then access or modify that value. A scope is the accessible scope of a variable or function.
There are two main working models of scope, lexical (static) scope and dynamic scope:
- Lexical scope: the scope is defined when it is defined, that is, when writing code, it is determined where the variable and block scopes are written.
JavaScript
The lexical scope is used. - Dynamic scope: Scope is determined at run time, for example
bash
,Perl
.
JavaScript has three types of scope:
- Global scope: outermost layer of code.
- Function scope: Creating a function creates a scope, whether you call it or not, as long as the function is created, it has its own scope.
- Block-level scope for ES6:
ES6
The introduction of thelet
和const
Key words and{}
Combine so as to makeJavaScript
Block-level scopes are available, as described below.
Global scope
The outermost layer is the global scope, which can be accessed anywhere in the script. Variables with global scope are also called “global variables.”
Let’s look at which variables have global scope:
- In the browser, there is a global object called Window in the global scope that you can use directly.
// Get the height of the document display area of the window
window.innerHeight
Copy the code
- Outermost functions and variables defined
var a = 1
console.log(window.a) // 1 -- the a declared by var becomes the global variable of the window
function func1 () {
console.log('hello')
}
func1() // hello
window.func1() // hello
Copy the code
- Variables assigned directly without keywords are automatically declared to have global scope, mounted on
window
On the object.
b = 2 // Global variables
function func1 () {
c = 2
}
func1()
console.log(window.b) / / 2
console.log(window.c) / / 2
Copy the code
Variables that omit keywords, no matter b outside the function or C inside the function, are global variables and are mounted on the window. However, such omitted keywords are not recommended because they are not standard and not good for maintenance.
Variable ascension
Reverse the a code above as follows:
console.log(window.a) / / output is undefined
var a = 1
Copy the code
In this case, a is accessible before the declaration, but the output is undefined, which is often referred to as “variable promotion”.
Variable promotion: Variables declared by the var keyword are considered to be declared at the top of the current scope (both in function and global scope), regardless of where they are actually declared.
Because the way the JS engine works is divided into two stages: compilation and execution:
- The code is parsed to get all declared variables;
- And then run it again.
So the following two pieces of code are equivalent:
console.log(a); / / output is undefined
var a =1;
/ / equivalent to the
var a;
console.log(a); / / output is undefined
a =1;
Copy the code
- for
var a = 1
, encountered by the compilervar a
New variables are declared in scopea
. - The compiler then generates the code for the engine to run, processing
console.log(a)
anda = 1
- Gets variables from the current collection of scopes when the engine runs
a
(at this point isundefined
) and toa
Assignment 1
Function scope
Function scope variables or internal functions, scope is function scope, external are closed, from the outer scope can not directly access the function scope, otherwise, the reference error exception will be reported. As follows:
function func1 () {
var a = 1;
return a
}
func1() // The inside of the 1 function is accessible
console.log(a) // Uncaught ReferenceError: a is not defined
Copy the code
Function declaration
In function declarations, the JS engine takes the function declaration before the code executes and generates the function definition in the execution context.
console.log(add(10.10)) // Return 20 normally
function add (a, b) {
return a + b
}
Copy the code
The code works, and function declarations can be read and contextually added before any code is executed, that is, function declarations are promoted (just as variable declarations were promoted earlier).
Functional expression
The function expression must wait for the line of code to execute before generating the function definition in the execution context.
console.log(add(10.10)) // Uncaught TypeError: add is not a function
var add = function (a, b) {
return a + b
}
Copy the code
Var add = function(){} In this case, add is a variable, so the declaration of the variable is also promoted to the top, while the assignment of the variable remains where it was, so the error is that the variable add is of the wrong type.
Function declarations and variable declarations
Function declaration promotion and variable declaration promotion have been mentioned before, and the phenomenon of using them. Here are some examples of both being used together:
test() // Execute function declaration
var test = function () {
console.log('Execute function expression')}function test (a, b) {
console.log('Execute function declaration')
}
test() // "Execute function expression"
Copy the code
The first test() prints “execute function declaration” and the second test() prints “execute function expression”, because it undergoes both function declaration promotion and variable declaration promotion (function promotion takes precedence over variable promotion). The code is equivalent to:
// Function declarations are promoted to the top
function test (a, b) {
console.log('Execute function declaration')}// Variable promotion, variable promotion does not override function promotion, only if the variable is reassigned
var test
// Still in the same place
test() // Execute function declaration
test = function () {
console.log('Execute function expression')
}
test() // "Execute function expression"
Copy the code
Block-level scope
ES6’s new let and const scopes are block-level scopes defined by the nearest pair of curly braces {}. Let is the following example:
{
var a = 1
let b = 2
}
console.log(a) / / 1
console.log(b) // Uncaught ReferenceError: b is not defined
Copy the code
Variables declared by let inside curly braces are not externally accessible, namely block-level scopes.
When a variable declared with the let keyword is pre-accessed:
{
console.log(a) Uncaught ReferenceError: A is not defined
let a = 1
}
Copy the code
The above error is caused by the “temporary dead zone” in let.
Temporary dead zone: a variable is not available until it is declared. Once it enters the current scope, the variable to be used already exists but cannot be retrieved until the line of code that declared the variable appears.
The above code can be equivalent to:
{
//let a, where the temporary dead zone begins
console.log(a) // Error because a = 2 is in temporary dead zone
a = 1 // Where the temporary dead zone ends
}
Copy the code
const
Const and let are the same in that they have “temporary dead zones” with the following restrictions:
const
When declaring a variable, it must be initialized to a value and cannot be reassigned.
<! --UncaughtSyntaxError: Missing initializer in const declaration -->
const a
Copy the code
- Assigned to an object
const
Variables cannot be assigned to any other reference value, but the key of the object is not restricted (Object.freeze()
Objects can be frozen completely, and key-value pairs cannot be modified.
const a = 1
a = 1 // Uncaught TypeError: Assignment to constant variable.
/ / object
const obj = {
a: 1.b: 2
}
obj.b = 3
console.log / / 3
Copy the code
Var, let, const
The difference | var | let | const |
---|---|---|---|
scope | Function scope | Block scope | Block scope |
The statement | The same scope can be declared more than once | The same scope cannot be declared more than once | The same scope cannot be declared more than once and must be assigned at the same time |
features | Variable promotion (global variables without var) | Temporary dead zone | Temporary dead zone |
Common example: the for loop
for (var i = 0; i< 5; i++) {
setTimeout(function() {
console.log(i)
})
}
// 5
for (let i = 0; i< 5; i++) {
setTimeout(function() {
console.log(i)
})
}
// 0 1 2 3 4
Copy the code
Var: global variable. The iterated variable holds the value at the time the loop exits. All I’s are the same variable at the time the timeout callback is executed.
Let: Block scope, the JS engine declares a new variable for each iteration loop, and each timeout callback calls a different variable instance.
Const cannot change the value, so we cannot use the for loop i++.
// Iterate over the object key
for (const key in {a: 1.b: 1{})console.log(key) // a b
}
// Iterate over the numbers
for (const val of [1.2.3]) {
console.log(val) / / 1 2 3
}
Copy the code
eval
Eval is generally not supported for use in code due to its poor performance, unsafe code, and confusing logic. However, it is important to understand eval.
This method is a complete ES interpreter that takes an argument, an ES (JavaScript) string to execute, parses the corresponding string into JavaScript code and runs it (parsing json strings into JSON objects). Simple uses of eval:
- If the argument is a string expression, the expression is evaluated
- If the argument is a string and represents one or more
JavaScript
Statement, then the statements will be executed - If the argument is not a string, the argument is returned unchanged
eval("2 + 2") / / output 4
eval("console.log('hi')") Hi / / output
eval(new String("2 + 2")) // String {'2 + 2'}
Copy the code
Eval’s impact on scope
Eval is called in JavaScript in two ways: directly and indirectly.
- When called directly:
eval
The scope of the inner code block is bound to the current scope and is used directlyeval()
.
function testEval () {
eval('var a = 111')
console.log(a) / / 111
}
testEval()
console.log(a) / / an error
Copy the code
We can get a inside testEval, so eval modifies the testEval scope.
- When invoked indirectly:
eval
The scope of the inner code block is bound to the global scope, usingwindow.eval()
(IE8 compatibility issues),window.execScript
(IE8 and below supported), to resolve compatibility issues, you can also assign variables globally and then use them inside functions.
// There is an IE compatibility problem
function testEval () {
window.eval('var a = 111')
console.log(a) / / 111
}
testEval()
console.log(a) // eval defines a variable bound to the global scope
// Resolve compatibility issues
var evalExp = eval
function testEval () {
evalExp('var a = 111')
console.log(a) / / 111
}
testEval()
console.log(a) // eval defines a variable bound to the global scope
Copy the code
Eval’s variable promotion problem
Any variables and functions defined by eval() are not promoted because they are contained in a string when parsing code. They are created only when eval() is executed.
Here are the different effects of let, var, and function, as follows:
/ / function
sayHi() // error: sayHi is not defined, no function is declared to promote
eval("function sayHi() { console.log('hi'); }");
sayHi() // hi
// var
msg // error: MSG is not defined
eval("var msg = 'hello world'")
console.log(msg) // hello world
// let
eval("let msg = 'hello world'; console.log(msg)") // // hello world
console.log(msg) // The error let scope can only be inside eval
Copy the code
The scope chain
Nesting of scopes occurs when a block or function is nested within another block or function. The query rules for nested scopes are as follows:
- First, the JS engine looks for variables from the current execution scope.
- Then, if it doesn’t find it, the engine continues the search in the outer nested scope.
- Finally, until the variable is found or the outermost global scope is reached.
A list made up of multiple scopes is called a scope chain.
Such as:
var c = 1
function func () {
var b = 2
function add (a) {
return a + b + c
}
return add
}
const addTest = func()
addTest(3) / / 6
Copy the code
The scope chain is:
When funcTest() is executed:
- To find the
add
Function scope to see if there area
, gets the value 3 passed into the scope - At this time to get
a
Continues the searchb
Find the value ofadd
Function scope to see if there areb
, there is no - Find the upper scope
func
To check whether there areb
, gets the value 2 of the current scope - At this point get
b
After the value ofc
The value of the inadd
C is not found in the scope of the function - Find the upper scope
func
, still can not query c - Search for the global scope at the next level, query a, query to get the value of scope 1
- Return to 6
closure
If you’ve noticed, JavaScript scopes are defined at the time of definition, so you can’t access variables outside of the function you’re defining. In the nested scope example above, addTest can access variables inside func because of the closure.
A closure is a function defined inside a function, and its scope can be remembered and accessed, even if the function is executed outside the current lexical scope.
var c = 1
function func () {
var b = 2
function add (a) {
return a + b + c
}
return add
}
const addTest = func()
addTest(3) / / 6
Copy the code
- First of all, the function
add()
The scope of thefunc()
The internal scope of - perform
func
, the inner functionadd
Assigns a reference to an external variableaddTest
At this time,addTest
The pointer is pointing to oradd
add
Still holding on tofunc
A reference to the scope, and this reference is called a closure- Execute externally
addTest
External executionadd
The closure allows access to the defined scope.
The original function func is not recycled when using closures and is included in the scope of add, so it takes up more memory than other functions and is prone to memory leaks
Use of closures
As you can see above, closures are everywhere in your code. Here’s how they work:
The callback
As in the let loop example above:
for (let i = 0; i< 5; i++) {
setTimeout(function() {
console.log(i)
})
}
Copy the code
The setTimeout callback remembers the current lexical scope, and when the loop ends and the function executes, it can access the I of the current scope
modular
// Get positive and reverse permutations in the array
function arrOperate () {
let errorMsg = 'Please pass in an array'
/ / positive sequence
function getPositiveArr(arr) {
if (Array.isArray(arr)) {
return arr.sort((a, b) = > {
return a - b
})
} else {
throw errorMsg
}
}
/ / reverse
function getBackArr(arr) {
if (Array.isArray(arr)) {
return arr.sort((a, b) = > {
return b - a
})
} else {
throw errorMsg
}
}
return {
getPositiveArr,
getBackArr
}
}
const arrObj = arrOperate()
arrObj.getPositiveArr([1.10.5.89.46]) // [1, 5, 10, 46, 89]
arrObj.getBackArr([1.10.5.89.46]) // [89, 46, 10, 5, 1]
arrObj.getPositiveArr(123) // Uncaught Please pass an array
Copy the code
This pattern is called a module in JavaScript, and arrOperate() returns an object containing a reference to the inner function, while the getPositiveArr() and getBackArr() functions have closures covering the inner scope of the module instance, Accessible errorMsg
conclusion
Scope determines the accessible scope of variables. Code can be seen everywhere. Understand scope, avoid using variables that cannot be accessed, and reduce errors at the beginning of the article.
The resources
- JavaScript You Don’t Know
- JaveScript Advanced Programming