JavaScript 是一种基于对象的动态弱类型脚本语言 (以下简称 JS), 是一种解释型语言, 和其他的编程语言不同, 如 java/C++ 等编译型语言, 这些语言在代码执行前会进行通篇编译, 先编译成字节码(机器码) 然后在执行而 JS 不是这样做的, JS 是不需要编译成中间码, 而是可以直接在浏览器中运行, JS 运行过程可分为两个阶段, 编译和执行 (可参考你不知道的 JS 这本书), 当 JS 控制器转到一段可执行的代码时(这段可执行代码就是编译阶段生成的), 会创建与之对应的执行上下文(Excution Context 简称 EC) 执行上下文可以理解为执行环境(执行上下文只能由 JS 解释器创建, 也只能由 JS 解释器使用, 用户是不可以操作该对象'的)
JS 中的执行环境分为三类:
全局环境: 当 JS 引擎进入一个代码块时, 如遇到 < script>xxx</script > 标签, 就是进入一个全局执行环境
函数环境: 当一个函数被调用时, 在函数内部就形成了一个函数执行环境
eval(): 把字符串单做 JS 代码执行, 不推荐使用
在一段 JS 代码中可能会产生多个执行上下文, 在 JS 中用栈这种数据结构来管理执行上下文, 栈的特点是先进后出, 后进先出, 这种栈称之为函数调用栈
执行上下文的特点
栈底永远是全局执行上下文, 有且仅有一个
全局执行上下文只有在浏览器关闭时, 才会弹出栈
其他的执行上下文的数量没有限制
栈顶永远是当前活动执行上下文, 其余的都处于等待状态中, 一旦执行完毕, 立即弹出栈, 然后控制权交回下一个执行上下文
函数只有在每次被调用时, 才会为其创建执行上下文, 函数被声明时是没有的
执行上下文可以形象的理解为一个普通的 JS 对象, 一个执行上下文的生命周期大概包含两个阶段:
创建阶段
此阶段主要完成三件事件, 1 创建变量对象 2 建立作用域链 3 确定 this 指向
执行阶段
此阶段主要完成变量赋值函数调用其他操作
变量对象 (VO) 的创建过程
1 根据函数参数, 创建并初始化 arguments 对象, 给 arguments 对象添加属性 "0","1","2","3" 等属性, 其初始值为 undefined, 并设置 arguments.length 值为实际传入参数的个数
2 查找 function 函数声明, 在变量对象上添加属性, 属性名就是函数名, 属性值就是函数的引用值, 如果已经存在同名的, 则直接覆盖
3 查找 var 变量声明(查找变量时, 会把函数的参数等价于 var 声明, 所以在 VO 中也会添加和参数名一样的属性, 初始值也是 undefined), 在变量对象添加属性, 属性名就是变量名, 属性值是 undefined, 如果已经存在同名的, 则不处理
如果存在同名标识符(函数变量), 则函数可以覆盖变量, 函数的优先级高于变量
变量对象 (OV) 和激活对象 (AO) 是同一个东西, 在不同时期的两种叫法在创建时期叫变量对象, 在执行时期叫激活对象
以如下代码为例
- var g_name = "tom";
- var g_age = 20;
- function g_fn(num) {
- var l_name = "kity";
- var l_age = 18;
- function l_fn() {
- console.log(g_name + '===' + l_name + '===' + num);
- }
- }
- g_fn(10);
编译阶段
当 JS 控制器转到这一段代码时, 会创建一个执行上下文, G_EC
执行上下文的结构大概如下:
- G_EC = {
- VO: {},
- Scope_chain: [],
- this: {}
- }
- /* VO 的结构大概 */
- VO = {
- g_name: undefined,
- g_age: undefined,
g_fn: <函数在内存中引用值 >
- }
- /* Scope_chain 的大概结构如下 */
- Scope_chain = [G_EC.VO] // 数组中第一个元素是当前执行上下文的 VO, 第二个是父执行上下文的 VO, 最后一个是全局执行上下文的 VO, 在执行阶段, 会沿着这个作用域链一个一个的查找标识符, 如果查到则返回, 否知一直查找到全局执行上下文的 VO
- /* this */
- this = undefined // 此时 this 的值是 undefined
执行上下文一旦创建完毕, 就立马被压入函数调用栈中, 此时解释器会悄悄的做一件事情, 就是给当前 VO 中的函数添加一个内部属性[[scope]], 该属性指向上面的作用域链
g_fn.scope = [ global_EC.VO ] // 该 scope 属性只能被 JS 解释器所使用, 用户无法使用
执行阶段
一行一行执行代码, 当遇到一个表达式时, 就会去当前作用域链的中查找 VO 对象, 如果找到则返回, 如果找不到, 则继续查找下一个 VO 对象, 直至全局 VO 对象终止
此阶段可以有变量赋值, 函数调用等操作, 当解释器遇到 g_fn()时, 就知道这是一个函数调用, 然后立即为其创建一个函数执行上下文, fn_EC, 该上下文 fn_EC 同样有两个阶段
分别是创建阶段和执行阶段
在创建阶段, 对于函数执行上下文, 在创建变量对象时, 会多创建一个 arguments 对象, 然后为 arguments 对象添加属性:"0","1", "2" 其初始值为 undefined,
查找 function 函数声明
查找 var 变量声明
来源: http://www.jb51.net/article/134801.htm