What is the call stack

  1. Js creates a global context during execution and pushes the global context onto the stack
  2. When a function is executed, an execution context for that function is created
  3. When a function completes execution, the execution context of that function is removed from the stack,
  4. During execution, the function execution context is continuously pushed in and out of the stack
  5. When global code execution is complete, push the global execution context off the stack. At this point, program execution is complete
    function func1() {}function func(b, c) {
      func1()
    }
    func()
Copy the code

The execution stack structure of the above code is as follows (see the call stack at the interrupt point inside func1). The bottom anonymous is the global execution context. During execution, the function execution context is continuously pushed off the stack until the stack is empty and the program is finished

Scope and scope chain

A scope is the accessible range of variables and functions in a program. There are three types of scopes

  • The global scope is accessible to any code
  • A function scope can only be accessed inside a function
  • Block-level scopes are available within scoped blocks

Note: The object is not a scope, just a data structure

var myname = "External"
function showName(){
  console.log(myname);
  if(0) {var myname = "Internal"
  }
  console.log(myname);
}
showName()  
// Print two undefined
Copy the code

Analyze the code above

  • The variable declared by var is promoted to the top of the current scope. Assignment is not promoted, so both prints access myname inside the function, so undefined is printed

The scope chain

First look at a piece of code and try to guess the output

    function bar() {
      console.log(name)
    }

    function foo() {
      var name = "Internal"
      bar()
    }
    var name = "External"
    foo()
Copy the code

After careful analysis of the first thought, the output should be internal, according to the search order of this variable, it must print ‘internal’, but the result is… So let’s do a step by step analysis, and let’s first look at what is a scope chain

Scope chain: The variable environment of each execution context contains a reference to the external execution context. For example, in the above example, name is not found in the bar function. The current external reference refers to the function context

According to the logic of scope chain, why find the variable whose name is external, should not be internal?

This is because the scope chain is determined by the lexical environment, and you need to know the order in which the variable’s scope chain is found, as well as the lexical scope

Lexical scope: The lexical scope is determined by the position of the function declaration in the code, so the lexical scope is static and depends on the position in the code structure, not the relative order in the call stack

In the above example, bar is defined globally, so the upper execution contexts of bar and Foo are both global execution contexts by lexical scope, so the scope chain is both foo -> global, so the problem is solved

Modify the above code as follows to put the bar function declaration inside the foo function

    function bar() {
      console.log(name)
    }

    function foo() {
      var name = "Internal"

      function bar() {
        console.log(name)
      }
      bar()
    }
    var name = "External"
    foo()
Copy the code

The search order for the function’s scope chain is bar –> foo –> global.

closure

Let’s start with some code:

    function foo() {
      var myName = " test1 "
      let test1 = 1
      const test2 = 2
      var innerBar = {
        getName: function () {
          let test = 'testGetName'
          console.log(test1)
          return myName
        },
        setName: function (newName) {
          let test = 'testGetName'
          myName = newName
        }
      }
      return innerBar
    }
    let global = 'global';
    var bar = foo()
    bar.setName(" test2 ")
    bar.getName()
    console.log(bar.getName())
Copy the code

According to the rules of lexical scope, getName and setName inside Foo can access variables in foo. When foo completes, foo’s execution context is removed from the stack, but since the two inside functions still reference variables defined in Foo, these variables are closures

So closures are, the inner function always has access to the variables declared by the outer function, and even after the outer function has finished executing, the variables that the inner function refers to the outer function are still stored in memory, and the set of variables is the closure, and those variables are contained in foo, so it’s called the closure of Foo. The closure is in the browser closure.

You can see that only myName and test1 traversals are included in the closure. Variables that are not used by the internal function do not reside in memory and are not included in the closure

One last piece of code, understanding scoped chains and closures

var bar = {
    myName:"time.geekbang.com".printName: function () {
        console.log(myName)
    }    
}
function foo() {
    let myName = " test1 "
    return bar.printName
}
let myName = " test "
let _printName = foo()
_printName()   // test
bar.printName()  // test
// Print test for both,
// For the first: Because the printName function that's returned is defined outside of the function, inside the bar, so by the rules of lexology, it's going to start in the scope that printName is defined in because there's only one global scope, so it's going to find the global myName, and notice that the object is not really a scope, it's just a data structure, The rules for scoping chain searches are lexical scoped
// For the second: as for the first, it explains that the object is not a scope, except for the execution context of the function, which is the global context, without closure operation
var bar = {
    myName:"time.geekbang.com".printName: function () {
        console.log(myName)
    }    
}
function foo() {
    let myName = " test1 "
    return function () {
        console.log(myName)
    }    
}
let myName = " test "
let _printName = foo()
_printName()   // test1
bar.printName()  // test

// Some changes were made to put foo's return function directly inside the function, so that when the return function looks for variables that do not exist in its own context, it looks up and finds foo's closure, followed by global
Copy the code

So far, these are the current understanding of these issues, may have a new understanding in a few days, constantly learning, constantly correcting and updating their understanding, is slow progress. Come on!!