利用 js 执行环境(浏览器环境或者node环境),执行 js 代码, 在 js 代码解析第一步 从 js 源码到 Parse (词法解析)步骤,执行环境会创建一个全局对象 GlobalObject 对象,包括一些基本类型,及 setTimeout console.log 等 api,以及众所周知的 window 对象,window 就是 GlobalObject 也称作go
变量提升的原理图
在图中第一步创建GlobalObject 对象时,就已经将js代码中的变量收集到 GlobalObject 对象中了 ,所以在代码逻辑中,在声明变量前执行该变量的代码,由于执行代码其实是运行环境的最后步骤,所以在声明前使用变量,是从 GlobalObject 中取该变量,它的值就是 undefined -- 即常说的变量提升。
针对函数的编译阶段: 当js引擎编译代码时,如下代码顺序:
`foo()
foo() { ... }`
并不会报错,而是全局寻找该函数申明的地方,并开辟内存空间(堆内存 Heap)保存该函数的父级作用域(函数上一层作用域)以及函数中的代码块。而函数调用的地方将在GO中存储为上文为该函数开辟内存的内存地址。
针对函数的执行阶段: 调用栈(栈内存 ECStack)中创建函数执行上下文(FEC)其中包括 AO(Activation Object -- 活跃对象),AO为该函数所拥有的对象,其中保存函数作用域中的传入参数,变量,FEC创建完成后,会执行函数代码,当函数中的代码执行完毕,当前的FEC会销毁(弹出调用栈),相应的当前函数的AO也会销毁。 注: AO和GO是两种VO,前者为私有Object,后者为全局Object,前者执行完随着当前FEC出栈被销毁,后者则长期存在。
面试题: `
function foo () {
// 打印结果为 undefined, 因为函数本身申明的 m 变量,在编译的阶段,函数会在生成的AO中生成 m: undefined的结果,
// 根据作用域链的查找规则,自身有,就在自身查找,不去父级作用域中查找,所以结果为undefined
console.log(m);
var m = 200
console.log(m);
}
var m = 100
foo()
`