scope

Simple understanding: A scope is the range of variables available

Why scope: The purpose is to prevent variables of different ranges from interfering with each other

JS contains two levels of scope

  1. Global scope

    1. An external scope that does not belong to any function is called a global scope
    2. Variables saved in global scope are called global variables and persist after creation unless manually removed
    3. Global variables can be accessed from any scope. For example, the one that has not been defined before readinglet const classIs thrown at runtime and cannot be read before setting the initial value.)
  2. Function scope

    1. The scope within a function is called the function scope
    2. Variables stored in the scope of a function are called local variables. Parameters are also local variables within a function

A function scope is a scope object created temporarily by the JS engine when it calls a function. This object holds the local variables of the function. When the function is called, the function scope object is released.

Characteristics of global variables: can be used repeatedly but easy to cause global pollution

var a = 10;
function fun1() {
	a = 100; // It is accessible in fun1
}

function fun2() {
	a = 3 // It is also accessible in fun2
}
fun1();
console.log(a) / / 100
a = 111;
console.log(a) / / 111
fun2();
console.log(a) / / 3
Copy the code

Features of local variables: They will not be polluted by external variables, but cannot be used repeatedly (I want to maintain a variable, but do not want to be accessed by external variables can not be achieved)

var total = 10;
function pay (m) {
	var total = 100;
  total -= m
  console.log(total)
}
fun1(10) / / 90
fun1(10) / / 90. The second call still returns 90
console.log(total); / / 10. External total and internal TOAL do not affect each other
Copy the code

Only {} of a function can form a scope

  • But not all {} can be scoped
  • Not all data inside {} can be local variables
var xiaoming = { For example, the {} of this object is not a scope
  name: 'xiao ming' // Attributes in objects are not local variables
} 
Copy the code
if (false) {
  var a = 10
}
​
console.log(a) Uncaught ReferenceError: A is not defined
​
{
  var b = 1
}
console.log(b) // undefined is also output
Copy the code

Block-level scope

It is worth noting here that const, let in ES6 forms a “block-level scope” in {} as follows:

{
  let x = 0
}

console.log(x) // Uncaught ReferenceError: x is not defined
Copy the code

But this is not really block-level scope.

There are two ways of saying it:

  1. This is because new syntactic variables are kept in a separate lexical environment.
  2. JS uses a closure to hold the variable and its execution when it lets.

Personally, I would probably prefer the first one

The scope chain

What is a scope chain: each function is defined with its own roadmap for finding variables, called a scope chain

How is the scope chain formed: all scopes available to a function are concatenated to form the scope chain of the current function

The function searches for a variable from inside to outside. Once it finds the desired variable in a scope, it stops the search and obtains the value of the variable. In particular, if the desired variable is not found in the entire scope chain of the function, but assignment exists, the variable is created globally.

// 3. Y is still not found in the global scope, but because there is an assignment, y will be created. var y
  function f1() {
    const x = 1             // find y in f1 scope
    function f2() {
      y = 2                 // 1. Find y in f2
    }
    f2()
  }
  // console.log(y) // Uncaught ReferenceError: y is not defined
  f1()
  console.log(y)            / / 2
Copy the code

In JS, scopes and scope chains are object structures, 👇

To summarize the scoped object lookup rules:

  1. Lookup from the inner scope to the outer scope
  2. If the data is found in one of the scope objects, the data is returned and the lookup ends;
  3. Uncaught ReferenceError: y is not defined
  4. If it is an assignment, and the top-level scope is always found but cannot be found, the variable is created under the top-level scope and the assignment is returned

closure

What is a closure

A closure is an object. A closure is a function scope object that is created temporarily each time an outer function is called. The reason the outer function on the object survives is because the scope referenced by the inner function object cannot be released.

Closures are used when a function holds a dedicated local variable that can be used over and over again without being contaminated by the outside world.

  1. Wrap the variable to be protected with an outer function and the inner function that uses the variable
  2. Returns the inner function object inside the outer function
  3. Call the outer function to assign the result (the returned inner function object) to a variable
function mother() {                // 1. The outer function wraps the inner variables and functions
  let total = 1000                  // 1.1 Internal variables
  return function (money) {        // 2. Return the inner function
    total -= money                 // 1.2 Internal functions
    console.log(With the `${money}Yuan, still remain${total}Yuan `)}}const pay = mother()               // 3. Call the outer function to assign the return inner function to pay
pay(10) // I spent 10 yuan, leaving 990 yuan
Copy the code

How closures are implemented

Let’s go back to the code above. When it encounters function, the underlying JS implementation translates it to new function and assigns it to mother, as shown in the figure above.

Let’s use a diagram to illustrate the execution process of the entire code mother

  1. Mother is defined as

  1. When mother is called, a temporary function scope object is created because it is a function object, and variables inside the function are added to the temporary function scope object

  1. Mother calls 2, which is the step to assign to totaltotal = 1000, so the temporary function is scoped within the objecttotalIt becomes 1,000

  1. When mother returns, a return is first encountered, indicating the end of the function. An internal return value is created in the scope of the function motherReturn valueIts value is undefined

  1. But becausereturnThere is also a function, so we need to create a function object before returning (assuming its memory address is 0x2136). This function object does not have name as an anonymous function, but as a return value,Return valueGet a reference to the anonymous function as the value to be returned

In the figure below, we can see that the returned anonymous function [[Scopes]] is interesting. It is an array structure, and the 0th bit in the Closure(mother) object contains total: 1000, which indicates that if we want to access a variable in the function in the future, we’ll start at 0 in the [[Scopes]] and go all the way to the top level scope Global (window).

  1. Mother completes and returns

First of all, the mother function has finished executing, which disconnects mother from its assigned temporary scope. However, this temporary scope still has variables that are referenced by the returned inner function, so it cannot be destroyed (note: unreferenced variables will no longer exist in this temporary scope)

  1. When the variable pay is called

We use the run-time scoped object to illustrate the above diagram

The second time we call it, total becomes 990. In this way, our total can only be used internally when the PAY function is called, and the external can’t access the total variable inside the pay function. In this way, our data is safe and pollution-free, that is, the problem of global variable data pollution is solved, and the problem of local variable data in the function cannot be reused is solved.

Since closures are so deep that they are almost impossible to find, they can cause memory leaks. To prevent memory leaks, we simply release the closures that are not needed as soon as possible — assigning the variables of the saved inner function objects to null.

function mother() {
  let total = 1000
  return function(money) {
    total -= money
    console.log(` used to${money}Yuan, still remain${total}Yuan `)}}let pay = mother()
pay(10)
pay = null // No need to release it
Copy the code

Conclusion:

Scope: The first is global scope. An external scope that does not belong to any function is called a global scope. Variables declared by var and functions defined by function in the global scope are mounted on the Window object. Variables in the global scope can be accessed by any scope (note that the let const class method throws errors before setting the initial value), and variables in the global scope, once defined, generally persist (until the host environment is closed). Variables defined in a global scope are called global variables. The advantage of global variables is that they can be used over and over again, but the disadvantage is that they can cause global pollution.

The second type is function scope. The scope within a function is called function scope (note that arrow functions do not count). Variables stored in the scope of a function are called local variables. Parameters are also local variables within a function. Variables defined in functions are called local variables. The advantage of local variables is that there is no pollution, but the disadvantage is that they cannot be reused.

Scope chain: Each function is defined with its own path to finding variables, called the scope chain. All the scopes available to a function are concatenated to form the scope chain of the current function.

Scope chain lookup rules:

  1. Lookup from the inner scope to the outer scope
  2. If the data is found in one of the scope objects, the data is returned and the lookup ends;
  3. Uncaught ReferenceError: A is not defined
  4. If it is an assignment, and the top-level scope is always found but cannot be found, the variable is created under the top-level scope and the assignment is returned

Closures: Closures are used when a function needs to hold a dedicated local variable that can be used over and over again without being contaminated by the outside world.

A closure is an object. A closure is a function scope object that is created temporarily each time an outer function is called. The reason the outer function on the object survives is because the scope referenced by the inner function object cannot be released.

Three steps to using closures:

  1. Wrap the variable to be protected with an outer function and the inner function that uses the variable
  2. Returns the inner function object inside the outer function
  3. Call the outer function to assign the result (the returned inner function object) to a variable

Assigning the variable of the saved inner function object to NULL solves the memory leak problem.