scope

What is scope?

Scope is a pointer to access a variable has access to the code space, in JS scope is reflected in the function, indicating access to a variable area, refers to the context of the execution environment; In layman’s terms, JS contains only two types of scopes: global and local

Ps: Some students ask about methods. In fact, methods are also variables, but the syntax of declarations is different

function fn(){}

var fn = function(){}
Copy the code

Global scope

In web applications, the global scope is our Window object; in Node, the global scope is our global object

var a = '123'
console.log(a) / / 123
console.log(window.a) / / 123
a = {}
window.a === a // true
Copy the code

Local scope

In js, there is only one kind of local scope, that is, function scope. In the context of the current function execution, only the internal variables of the current function and the variables of the nested scope can be accessed (scope chain will be explained later). The scope chain of the current function is determined by the time the function is declared, not when the function is called

var a = 'window'
function fn1(){
    var a = 'fn1'
    console.log(a) // fn1
}
fn1()
Copy the code

Block-level scope (ES6)

Block-level scope is a new feature in ES6 that takes advantage of keywords such as const and let to create a block-level scope in the {} braces of the current declared context when declaring a variable

What problem does block-level scope solve?

Suppose we have a requirement that I want the function to hold the values of a loop one at a time and then print them out at the end

var callbacks = []
for(var i = 0; i < 10; i++) {
  callbacks.push(function(){
    console.log(i)
  })
}
callbacks.forEach((fn) = >{
  fn()
})
// Output 10 times 10
Copy the code

As a result, we found that I was declared with var, so it was a global variable. After the 10th loop, the global variable had changed to 10, which was different from our original intention, so we used let to try

var callbacks = []
for(let i = 0; i < 10; i++) {
  callbacks.push(function(){
    console.log(i)
  })
}
callbacks.forEach((fn) = >{
  fn()
})
// 0 1 2 3 4 5 6 7 8 9
Copy the code

This time it’s exactly what we expected. Why is that? First of all, let creates a new block-level scope for the context in the current {} and assigns the variable I. According to the proximity principle of scope chain, I will find the variable I declared in the closest scope to it each time:

Variable promotion (supplement)

This is actually a very big topic, you can refer to this article js variable promotion if you have the opportunity to talk about it later

The scope chain

The JS engine will first look for references to variables in the current scope according to the nesting relationship between scopes, and follow the nearby principle. If no reference is found, it is like a chain to search up the used variables

var a = 'window';
function fn1() {
  console.log('fn1:', a); // a -> window
  function fn2() {
    var a = 'fn2a'
    console.log('fn2:', a); // a -> fn2a
    function fn3() {
      console.log('fn3:', a); // a -> fn2a
    }
    fn3();
  }
  fn2();
}
fn1();
// Execution result:
/ / fn1: window
/ / fn2: fn2a
/ / fn3: fn2a
Copy the code

Scope chain:

closure

What is a closure?

Closures allow you to access the scope of an outer function in an inner function

function init() {
    var name = "window"; // Name is a local variable created by init
    function displayName() { // displayName() is an inner function, a closure
        alert(name); // Use a variable declared in the parent function
    }
    displayName();
}
init();
Copy the code

Init () creates a local variable name and a function called displayName(). DisplayName () is an internal function defined in init() and is only available inside the init() function. Note that displayName() does not have its own local variable. However, because the scope chain is nested it can access the variables of the external function, so displayName() can use the variable name declared in the parent function init().

Here’s another example:

function makeFunc() {
    var name = "window";
    function displayName() {
        alert(name);
    }
    return displayName;
}

var myFunc = makeFunc();
myFunc();
Copy the code

Running this code has exactly the same effect as the previous example of the init() function. What’s different (and interesting) is that the inner function displayName() returns from the outer function before it executes.

At first glance, it may not be intuitive that this code works. In some programming languages, local variables in a function exist only for the duration of the function’s execution. Once makeFunc() is executed, you might think that the name variable will no longer be accessible. However, because the code still works as expected, the situation is obviously different in JavaScript.

The reason is that functions in JavaScript form closures. Closures are a combination of functions and the lexical environment in which they are declared. The environment contains any local variables that were in scope when the closure was created. In this case, myFunc is a reference to the instance of the displayName function created when makeFunc is executed. An instance of displayName maintains a reference to its lexical environment, where the variable name exists. Therefore, when myFunc is called, the variable name is still available and its value window is passed to alert.

The principle of closures

To sum up, the internal function references the variable of the external function, and the internal function is returned and referenced by the global variable myFunc, forming a reference chain. As a result, the JS garbage collection mechanism cannot reclaim the variable name in the external function, thus forming an external function that cannot be accessed by others. But a closed environment that can be accessed by an internal function is a closure

Practical closures

Closures are useful because they allow functions to be associated with some data (the environment) on which they operate. This is obviously analogous to object-oriented programming. In object-oriented programming, objects allow us to associate certain data (properties of objects) with one or more methods. So in summary when you need a function to access a private scope, you can use closures

Disadvantages of closures

Because closures prevent references to external variables in a function from being collected by the garbage collection mechanism, they still take up memory and consume a lot of memory. Therefore, closures should not be abused. The solution is to delete unused local variables before exiting the function.