深入JS - 执行上下文、词法环境、闭包其实很简单

词法环境 === 作用域

(一)运行代码过程分为三步:

1. 初始化上下文
2. 注册 var 变量和 function 函数
3. 执行代码

(二)Javascript 程序执行过程

1. 全局上下文压入调用栈

callStack = [globalContext]

2. 运行全局上下文代码:

1) 初始化上下文:生成一个Global Environment 全局词法环境

        Global-Environment = {  
            record: Global-Object  
            outer: null  
        }
  • 该词法环境的数据记录在 Global-Object
  • 该词法环境的外部环境 outer 为 null

2) 注册 var 变量和 function 函数:变量提升

var a = 1  
...
function fn(){}
...
  • a 注册并初始化为 undefined
  Global-Object = {
    a: undefined
  }
  • fn 注册并生成一个 fn对象,内置属性 [[scope]] 指向创建fn时的词法环境
    Global-Object = {
      a: undefined,
      fn: fn-Object
    }
    fn-Object = {  
      [[scope]]: Global-Environment  
    }

3) 运行其他代码:

  • 若遇到赋值表达式,则进行赋值
    例如 a = 2

    Global-Object = {
      a: 2,
      fn: fn-Object
    }
  • 若遇到函数调用则压入调用栈

3. 函数上下文压入调用栈

callStack= [globalContext,fnContext]

4. 运行函数上下文代码:

1) 初始化上下文:生成一个 fn-Environment 词法环境

fn-Environment = {  
   record: fn-Map  
   outer: fn-Object.[[scope]]  
}  
fn-Map = {}
  • 初始化一个词法记录表fn-Map。
  • 词法环境中,outer 指向了该函数注册的对象 fn-Object 中的内置属性 [[scope]]

2) 注册 var 变量和 function 函数:过程同上

3) 运行其他代码:过程同上

4) 运行完毕后出栈,该函数词法环境被垃圾回收机制清除干净。

callStack = [globalContext]

(三)使用闭包保存词法环境

  • 函数上下文在函数执行后就弹出调用栈了,那么如何保存函数的词法环境呢?
  • 答案是:用一个全局变量引用该词法环境,该词法环境就不会被垃圾回收机制清除了。

首先在 fn 中创建一个新函数并生成一个 newFn-Object 对象,内置属性 [[scope]] 指向创建fn时的词法环境(即 fn-environment ),这样词法环境就被 newFn-Object记录下来了

    fn-Map = {  
       newFn = newFn-Object  
    }  
    newFn-Object = {  
       [[scope]]: fn-environment  
    }

然后在函数 fn 中把 newFn 返回出去,由全局环境接收

  // fn  
  fn = function (){  
    var x = 1  
    return function(){  
     return x  
    }  
  }  
  //global  
  const getX = fn()

这样就保证了 getX 对 newFn-Object 的引用,继而将 fn 的词法作用域保存了下来。

你可能感兴趣的:(javascript,闭包,作用域,作用域链,前端)