预备知识
首先我们看看 V8引擎的运行图

- V8引擎将js源码转成AST抽象语法树时,会创建全局对象Global Object(GO)
- 代码执行时,V8引擎创建一个执行上下文栈Execution Context Stack(ECStack)
- 若执行全局代码,创建全局执行上下文Global execution context(GEC)
🚧🚧注意:不同ES版本的区别
- 早期ES3版本: 由Scope作用域、Variable object变量对象、This三个部分组成
- 在ES5+的版本中: 由词法环境(lexical environment):存let/const声明的变量、变量环境(variable environment):存var声明的变量、This三个版本组成
- ES2018中:This被归入词法环境(lexical environment) 虽然不同版本的ES定义不同,但是也不妨碍JS语言的思想,下文用ES3版本进行解读
在js的执行上下文中,假设有一段代码是这样的
var name = "akechi"
foo(123)
function foo(num){
console.log(m)
var m = 10
var n = 20
console.log("foo")
}
JS的解析过程
首先进行全局的解析,创建ECStack并把区域的变量VO放入到全局对象GO中
- 变量设置为undefined
- 函数比较特殊,存放一个地址,指向函数内代码块存放的区域

开始执行
- name属性赋值
- 创建foo函数的执行上下文,初始化foo函数内的变量

执行foo内部函数
- foo内部变量赋值
- 执行foo内部的函数

执行结束
- 输出结果打印
- foo函数 弹出调用栈,等待GC回收

那如果是嵌套函数呢
var name = "akechi"
foo(123)
function foo(num){
console.log(m)
var m = 10
var n = 20
function bar(){
console.log(name)
}
bar()
}
图解如下

- js会在调用栈中加入bar函数,并初始化它
- bar函数打印name时沿着作用域链寻找,在全局找到name变量并打印
- 在依次执行完bar与foo函数后,弹出调用栈
🚧🚧注意:作用域链在静态解析阶段就已经确认下来了,与在哪调用无关
举个🌰
var message = "Global"
function foo(){
console.log(message)
}
function bar(){
var message = "bar"
foo()
}
bar() //打印的结果是Global
图解如下,foo函数的父级作用域时全局对象GO
