js作用域链

<body>
<!--
1. 理解
  * 多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)
  * 查找变量时就是沿着作用域链来查找的
2. 查找一个变量的查找规则
  * 在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
  * 在上一级作用域执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
  * 再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常
-->
<script type="text/javascript">
  var a = 1
  function fn1() {
    var b = 2
    function fn2() {
      var c = 3
      console.log(c)
      console.log(b)
      console.log(a)
      console.log(d)
    }
    fn2()
  }
  fn1()
</script>

</body>

 

 

当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。

我们已经知道每次调用函数都会新建一个执行环境。但是在JavaScript解析器内部,调用执行环境要经历两个阶段:

第一阶段: 创建阶段(Creation Stage) - 当函数被调用,但尚未执行函数体内的代码时:

创建作用域链(Scope Chain) 创建变量、函数和参数 确定this的值

第二阶段: 活动 / 代码执行阶段(Activation / Code Execution Stage) 

 

在 JavaScript 代码执行前,执行上下文将经历创建阶段。在创建阶段会发生三件事

  • this 值的决定,即我们所熟知的 This 绑定。
  • 创建词法环境组件。(1) 环境记录器(存储变量和函数声明的实际位置)和 (2) 一个外部环境的引用(就是作用域)。
  • 创建变量环境组件。

抽象地讲,词法环境在伪代码中看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GlobalExectionContext = {
 LexicalEnvironment: {
  EnvironmentRecord: {
   Type: "Object",
   // 在这里绑定标识符
  }
  outer: <null>
 }
}
 
FunctionExectionContext = {
 LexicalEnvironment: {
  EnvironmentRecord: {
   Type: "Declarative",
   // 在这里绑定标识符
  }
  outer: <Global or outer function environment reference>
 }
}

 

JS权威指南指出”JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.” 
ECMA262中所述 任何执行上下文时刻的作用域, 都是由作用域链(scope chain)来实现.在一个函数被定义的时候, 会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性.
在一个函数对象被调用的时候,会创建一个活动对象(也就是一个对象), 然后对于每一个函数的形参,都命名为该活动对象的命名属性, 然后将这个活动对象做为此时的作用域链(scope chain)最前端, 并将这个函数对象的[[scope]]加入到scope chain中.

posted @ 2019-10-13 13:18  燕子fly  阅读(147)  评论(0)    收藏  举报