This article will use a map to sort out the context of the book, because it is a guide, the body part will only list the key content, non-key content will be briefly introduced, welcome to discuss and read the original text. In addition, this article is suitable for students who have not read this book to refer to whether they need to read it. In addition, students who have read this book can try to answer the questions at the beginning of the article and recall the content of the book by following the guide. If it is very smooth, I believe that your understanding of the knowledge in the book is good.
In the last article, we looked at scopes from chapter 1 of the book, scope chains, and briefly explained how engines, compilers, and scopes work together to compile. In this part we will introduce chapters 2 to 6 of the first part of the book.
The problem
- Tell me what you know about closures. (interview)
- Tell us about your understanding of lexical scope. How can we cheat lexical scope?
- What is the effect of a function scope? How do I avoid function scope contaminating global variables?
- What block scopes do JavaScript have besides function scopes?
- What is the mechanism for variable promotion? Which function declaration or variable is promoted first?
The first part is scopes and closures
What do you know about closures? If you don’t know closures, you can skip them for now.
A closure is a function that is bound to its execution environment. The difference between a closure and a normal function is that it carries the execution environment.
A closure consists of an environment and an expression.
- Part of the environment
- Context: The lexical scope of a function (part of the execution context).
- Identifier list: Undeclared variables used in a function.
- Expression part: function body.
It is widely used in callback functions such as timers, event listeners, Ajax requests, cross-window communication… We know about loops, we know about modules. Closures give us the ability to access and manipulate parent scopes, which makes development a lot easier.
The downside to closures is that they can be difficult to maintain. Closures can cache variables in a parent scope. If the closure executes asynchronously, it’s important to know what’s going on in the parent scope and understand how the code works and the logic.
1. Lexical scope?
-
Scope is divided into lexical scope and dynamic scope.
A lexical scope is a set of rules about how and where an engine finds variables. The most important feature of a lexical scope is that it defines procedures that take place at the writing stage of code (assuming eval() or with is not used).
Dynamic scoping lets scopes be determined dynamically at run time.
The lexical stage is when you say a sentence and the compiler breaks it. Just as different people understand different sentences differently, so do different compiler participles.
-
There are environment loggers and references to the external environment in lexical scope. To understand this, we need to understand the execution context and execution stack (not mentioned in the book).
-
Lookup process
function foo(a) { var b = "Java" function bar(c) { var b = "Hello" console.log(a + b + c) } bar("Script") } foo("You don't know.") Copy the code
The “masking effect” scope lookup stops when the first matching identifier is found. We can define identifiers with the same name in multiple nested scopes, and we may not find external identifiers due to the masking effect.
“Global object” Global variables automatically become properties of the global object, so they can be looked up without directly using the lexical name of the global object.
window.a Copy the code
This way we can access Shadowed global variables, but non-global variables that are Shadowed cannot be accessed anyway.
-
How to change lexical scope?
The lexical scope is defined entirely by the position of the function declaration during code writing. How can it be changed at run time?
There are two ways to change lexical scope, eval and with
JavaScript engines perform several performance optimizations at compile time. Some of these optimizations rely on static lexical analysis and must be pre-determined where all variables and functions are defined. Once these optimizations are found in the engine, these optimizations are invalid.
So these two ways are not recommended to use, want to see the students can look up information or browse the book.
Function scope and block scope
-
What is the scope of the function?
Each declaration of a function creates a scope for itself, in which all variables belonging to the function can be used and reused throughout the scope of the function (including nested scopes).
-
What is the effect of a function scope?
Hidden, variables or functions declared in the current function scope are bound to the current function scope, so that they are not accessible in the global scope, we can call this function wrapped function.
This is consistent with the principle that software design should expose as little as necessary and “hide” everything else, such as module or object API design.
Avoid conflicts between identifiers with the same name.
-
What does the function expression do immediately?
(function foo() { var a = "zhengyang"; console.log(a); }) ();Copy the code
Since the function is an expression when contained in a pair of (), it can be executed immediately by adding a () to the end. It prevents the foo function name from contaminating the global scope and does not need to be called by the foo() function name.
The community calls it LIFE, and we tend to use anonymous functions, omitting foo, because function expressions can be anonymous.
-
Let mechanism?
The let keyword binds variables to any scope they are in (usually in {}), so that let can implicitly hijack the block scope for the variables it declares. (JavaScript does not have block scope functionality, but with, try/catch can create block scope)
Three,
-
All declarations, including variables and functions, are processed first before any code is executed.
-
Function declarations take precedence over variable declarations.
We think of var a = 2 as a statement, and in fact this expression can be broken down as var a; And a = 2, the first part of the variable declaration takes place at compile time, and the second part of the assignment is left in place for execution.
The console. The log / / ƒ (zy)zy() {} function zy() { } console.log(zy); / / ƒzy() {} var zy = 2 console.log(zy); / / 2Copy the code
Function zy(){}, then var zy, and then execute it one by one, so a console.log(zy) outputs the function, Then the second console.log(zy) outputs the function, and finally we assign 2 to ZY with the zy = 2 left in place so the last console.log(zy) outputs 2.
Loops and closures
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i)
}, i * 1000);
}
Copy the code
It’s not hard to understand that the output of the above code is five 5’s, because setTimeout is asynchronous and will not be executed until after the for loop ends. But what we want to get is one output per loop, one, two, three, four, five.
Use closures to solve the problem.
for (var i = 1; i <= 5; i++) {
(function () {
var j = i;
setTimeout(() =>{
console.log(j)
}, j * 1000)
})();
}
Copy the code
In the above code our environment is global scoped, the function body is an immediate function, and each loop I’s value is retained by the closure and passed to J and then printed once via setTimeout.
for (var i = 1; i <= 5; i++) {
(function (j) {
setTimeout(() =>{
console.log(j)
}, j * 1000)
})(i);
}
Copy the code
We can also use let.
for (let i = 1; i <= 5; i++){
setTimeout(() =>{
console.log(i)
}, i*1000)
}
Copy the code
After the speech
I have a new understanding of closure, but there are still many problems, such as the specific compilation process; How to optimize JavaScript engines; The understanding of synchronous asynchrony is still to be explored, and the implementation of let is not well understood.