1.js运行的三部曲
首先,我们需要了解JavsScript在浏览器中运行的三个步骤
语法分析 ==> 预编译(重点)===>解析执行
- 语法分析:整体分析js语法有没有错误,有的话会立即报错并停止运行
- 预编译(重点):主要解决执行顺序的问题;(如变量声明提升,函数声明提升等)
- 解析执行
2.执行环境及作用域
执行环境也叫执行期上下文。 在这,我们需要先了解几个JavaScript中的几个重要概念
- 执行期上下文(AO):当函数在执行的前一刻,会创建一个执行期上下文的内部对象。一个执行期上下文对象定义了一个函数执行时的环境,函数每次执行时对应的执行期上下文都是独一无二的,所以每次调用同一函数会导致创建多个执行期上下文对象,当函数执行完毕,执行期上下文被销毁。
- 作用域([[scope]]):每个JavaScript函数都是一个对象,对象中有些属性我们可以访问,有些我们不可以,这些属性只供JavaScript引擎存取。[[scope]]就是其中一个,[[scope]]就是我们所说的作用域,其中存储了执行期上下文的集合。一个函数被定义时,它的[[scope]]指向的对象(scope chain)的第0位存储的就是该函数所在的执行环境。
- 作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
- 查找变量:在哪个函数里边查找变量,就从哪个函数作用域链的顶端依次向下查找。
例:





3.预编译
3.1全局预编译
- 创建GO对象(Global Object,js中windowGO可以理解为Window);
- 找变量声明,讲变声名作为GO的属性名,值为undefined;
- 找函数声明,函数名作为GO的属性名,函数体作为GO的属性值
3.2函数中的预编译
- 创建AO对象(Active Object)(AO也叫执行期上下文)
- 找形参和变量声明,将变量和形参作为AO的属性名,值为undefined
- 将实参和实参统一
- 在函数中找函数声明,函数名作为GO的属性名,函数体作为GO的属性值
4.闭包
- 定义:当内部函数被保存到外部时,就生成了闭包。
- 闭包的作用:
- 实现共有变量(累加器)
- 可以做缓存(存储结构)
- 可以实现封装和属性私有化
- 模块化开发,防止污染全局变量(立即执行函数)
- 闭包的防范: 闭包会导致原有的作用域链不释放,造成内存泄漏; 闭包会导致多个执行函数公用一个私有变量,如果不是特殊需要,应尽量避免这种情况发生。