
七、JavaScript之深入理解作用域链
一、从代码运行看作用域
- js引擎在执行js代码之前,编译器会先对js代码进行词法分析、语法分析和代码生成。
- 在词法分析会把一段代码分解成词法单元,然后把词法单元(token)解析成树结构
- 在代码生成式时遇到变量,会把变量存在当前环境作用域内并生成引擎可以执行的代码
- 在执行代码时会先去作用域中查当前变量是否存在,之后进行下一步操作。
- 比如遇到var a=1;这段程序时。
- 编译阶段,编译器遇到var a的时候会先进行变量提升,把a放到当前执行环境的作用域内。
- 执行阶段 遇到a=1;会先去作用域内查,是否有变量a,如果有,就把1赋值给它。
二、作用域链
1 | function f1(a){ |
- 编译阶段,编译器遇到function和var时,会先获取这些变量的定义,把b和f1函数放入全局作用域内。
- 引擎执行代码时,
- 遇到b=2,在作用域内查找变量b,并将2赋值给b。
- 遇到f1(1)时,会进入f1函数的执行上下文,进行执行f1,当执行到console.log时,调用栈的环境如下图所示。
在执行console.log( a + b +c )时,引擎查找b时,会现在f1函数的作用域范围内查找,找不到就会去全局作用里查找。在全局作用域里,找到b=2。这就是所谓的作用域链式查找。
三、作用域链形成规则
1 | function f2() { |
我们执行后发现f2虽然嵌入在f1函数里,但是f2里的myName取值并不是f1中的myName,而是全局的变量myName。
注意这里就涉及一个重要的概念:
- 作用域链是由词法作用域决定的,而词法作用域就是指作用域由代码中函数声明的位置来决定的,所以词法作用域也成为静态作用域。(也可以理解为在词法生成阶段就已经决定了作用域的位置。也就决定了作用域链)
当执行到console.log(myname),调用栈信息如下图所示
- 每个作用域里都有一个outer指向它的上一级作用域,这个指向是按照代码书写位置来决定的。
- 也就是说作用域链由代码书写位置决定,和函数调用没有关系。
1 | 1: function createCounter() { |
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 MichstaBe