Introduction to the

You Don’t Know JavaScript series of reading notes

Scope and closure

Chapter 1 – What is scope

Engine & compiler & scope

Let’s start with a couple of concepts

  • Engine: Responsible for compiling and executing the entire JavaScript program.
  • Compiler: responsible for parsing and code generation and other dirty work
  • 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.
  • Scope nesting: When a block or function is nested within another block or function, the engine looks for variables starting at the current execution scope and continuing up one level if it cannot find them. When the outermost global scope is reached, the search process stops whether it is found or not.

Let’s take a closer look at some concepts through the following statements

var a = 2
Copy the code

This statement process is divided into two stages. In the first stage, the compiler declares the scope – looks for a variable in the current scope, ignores the declaration if it exists – continues compiling, and declares a new variable if it does not exist – names a. In the second phase, the engine asks if the scope has a. If it does, use the variable and assign a value, otherwise it throws an exception.

RHS query & LHS query

RHS query: when a variable appears on the right side of an assignment, who is the source of the assignment

Consider the following code: console.log(a)

The reference to A is an RHS reference because there is no value assigned to A.

In contrast, a =2 is an LHS reference to a, because we don’t really care what the current value is, we just want to find a target for the =2 assignment, okay

Look at a little 🌰

RHS-> foo, LHS-> a = 2, and RHS-> console.log(a)

Look at a little 🌰

Why are LHS and RHS distinguished

First, compare the following two pieces of code

� � Function foo(a) {console.log(a + b) b = a} foo(2) � Function foo(a) {console.log(a + b) // 4} foo(2)Copy the code

The code above shows that in non-strict mode, the first code will be abnormal

The second piece of code will execute normally

The reason is that the engine throws ReferenceError if the RHS query does not find the desired variable in all nested scopes. When the engine executes an LHS query, if the target variable cannot be found at the top level (global scope), the global scope creates a variable with that name and returns it to the engine, provided the program is not running in “strict mode.”

Chapter 2 – Lexical scope

Define the scope at the lexical stage. In other words, the lexical scope is determined by where you write the variable and block scopes when you write the code.

The code above can be divided into the following three lexical scopes

But there is an exception, spoofing scope

eval

In strict mode programs, eval(..) At runtime, it has its own lexical scope, meaning that declarations within it cannot be modified to the scope they are in.

with

If the with function does not declare a local variable, the assignment will leak to the global scope.

Chapter 3 is function scope and block scope

Meaning of function scope

The meaning of a function scope is that all variables belonging to the function can be used and reused throughout the scope of the function (in fact, nested scopes can also be used)

In the example above, function foo has a separate function scope, including a, b, c, and bar. So calling parameters a, b, c, or bar directly from the global scope will prompt an error, which is what the function scope does.

The scope of a function

Hide internal implementation: minimum authorization or minimum exposure principle

The original method exposes the Global doSomethingElse method and the b variable, but these two are currently only used in doSometing functions, so there is no doubt that doSomethingElse and B pollute global variables. We can circumvent this problem by using the scope of a function

The current doSomethingElse methods and b variables can only be called within the doSometing function and are not available globally

To avoid conflict

You can avoid overwriting problems caused by the same variables

Consider a question: What if a function didn’t need a function name (or at least a function name that didn’t pollute its scope) and could run automatically? By now, I’m sure you’ve figured out a solution – anonymous and named

The first is the timer function that we see all the time

Of course, we can add a function name to the timer function for easy comprehension and readability, but it has no effect on the function execution.

There is also a common pattern, the immediate execution of the function, is also the use of the functions and characteristics of the variable encapsulation and isolation.

Meaning of block scope

Functions are not the only unit of scope. Block scoping means that variables and functions can belong not only to the scope they are in, but also to a code block (usually {.. } inside) in fact, the concept of block scope is relatively vague or undefined to javascript developers in ES6. In fact, we used try.. A catch in a catch method is a block scope. Variables in a catch can only be used in a catch method and cannot be retrieved externally.

After ES6 we have let and const to declare variables that can block the current block scope, as shown below

Chapter 4 Ascension

Variable promotion. All declarations, including variables and functions, are processed first before any code is executed. Only the declaration itself is promoted, while assignment or other runtime logic is left in place. The following example can be executed in non-strict mode.

1. Let and const declare variables without variable promotion

In the following example, var is used to declare variable A in the if statement, and let is used to declare variable B.

Function declarations are promoted, but function expressions are not

Function declarations and variable declarations are improved. Functions are promoted first, then variables

4. Function declarations inside a normal block are usually promoted to the top of their scope

Chapter 5. Scope closures

Closures occur when a function can remember and access its lexical scope, even if the function is executed outside the current lexical scope.

So let’s think about, for example, is this a closure?

Technically, the way to interpret a reference to a by bar() is a lexical scoped lookup rule that is only part of the closure

In purely academic terms, the function bar() has a closure that covers the scope of Foo () (in fact, all scopes it can access, such as global scopes). The reason is straightforward, because bar() is nested inside foo().

Let’s modify the example to make closures more intuitive

  • The lexical scope of the function bar() has access to the internal scope of foo()
  • After foo() is executed, its return value (the internal bar() function) is assigned to the variable baz and baz() is called
  • Bar () can obviously be executed outside of its own lexical scope
  • After foo() is executed, it is usually expected that foo() ‘s entire internal scope will be destroyed, thanks to the declared location of bar(), which has a closure covering foo()’ s internal scope, allowing the scope to remain alive for bar() to reference at any time thereafter
  • Bar () still holds a reference to the scope, and the function is called outside the lexical scope at definition time. Closures allow functions to continue to access the lexical scope at which they were defined, and this reference is called a closure

Let’s look at two examples to get a better understanding of this, without going into the details

Is the following immediately executed function a closure?

  • IIFE is often considered a classic example of a closure, but it is not strictly a closure.
  • The function (IIFE in the sample code) is not executed outside its own lexical scope. It executes in the scope in which it was defined (and the external scope, that is, the global scope, also holds a).
  • A is found by ordinary lexical scope lookup rather than closure.
  • While IIFE itself is not a good example of looking at closures, it does create closures and is the most commonly used tool for creating closures that can be enclosed