注意:活动对象、this、scope都是独立的哦!
例子1:
>var a1 = 1;
>function fun(num) {
var a2 = num;
return a2;
}
>fun(2) // 调用fun函数
预编译阶段:创建了环境相对应的变量对象、作用域链初始值、this对象,另外变量声明提升也是这个阶段做的。
1、js解析器,创建一个变量对象(与全局环境相对应),它由定义在执行环境中的变量(a1: undefined)、函数(fun)组成,我们无法手动访问它,只有js解析器能访问到它。
2、js解析器,创建一个变量对象(与函数fun环境相对应),它由定义在执行环境中的每个形参值(数组)arguments: [ ]、显式声明的变量(a2: undefined)和函数组成,我们无法手动访问它,只有js解析器能访问到它。
3、js解析器,在函数fun内,创建一个预先包含外部对象(包含全局活动对象和包含自己的对象的活动对象)的作用域链(包括全局活动对象),这个作用域链被保存在内部的[[scope]]属性中,我们打印函数时可以看到。
4、js解析器,创建一个this对象,未绑定关联值。
执行阶段:执行流进入函数fun,函数fun的执行环境(其实就是变量对象)会被推入一个环境栈中,开始执行函数。
1、js解析器,创建一个活动对象(与全局执行环境相对应)。它的初始值,就是预编译时,全局环境的变量对象。
2、js解析器,在函数fun内,创建一个活动对象(与函数fun执行环境相对应)。它的初始值,就是预编译时,函数fun环境的变量对象。
3、js解析器,在函数fun内,复制[[scope]]属性中的对象,构建执行环境的作用域链,并把自己的活动对象推向当前作用域链的前端以此形成完整的作用域链。
4、js解析器,在函数fun内,给this对象,绑定关联值,即调用该函数的环境对象。
函数fun执行结束:而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
1、js解析器,销毁活动对象(与函数fun执行环境相对应),释放内存。
全局环境执行结束:页面或者浏览器被关闭时,销毁全局执行环境。
1、js解析器,销毁活动对象(与全局执行环境相对应),释放内存。
例子2:(闭包情况)
>function fun1(num) {
var a2 = num;
return function fun2() {
a2 = a2 + 1
console.log('a2:' + a2)
}
}
>var fun = fun1(2) // 调用fun1函数
>fun() // a2: 2
>fun() // a2: 3
我的天,好复杂!没关系我们按上面的步骤,一步步来看!
预编译阶段:创建了环境相对应的变量对象、作用域链初始值、this对象,另外变量声明提升也是这个阶段做的。
1、js解析器,创建一个变量对象(与全局环境相对应),它由定义在执行环境中的变量(fun1, fun, value)、函数(fun)组成,我们无法手动访问它,只有js解析器能访问到它。
2、js解析器,在函数fun1内,创建一个变量对象(与函数fun1环境相对应),它由定义在执行环境中的每个形参值(数组)arguments: [ ]、显式声明的变量(a2: undefined)和函数(fun2)组成,我们无法手动访问它,只有js解析器能访问到它。
3、js解析器,在函数fun1内,创建一个预先包含外部对象(包含全局活动对象和包含自己的对象的活动对象)的作用域链(包括全局活动对象),这个作用域链被保存在内部的[[scope]]属性中,我们打印函数时可以看到。
4、js解析器,在函数fun1内,创建一个this对象,未绑定关联值。
5、js解析器,在函数fun2内,创建一个变量对象(与函数fun2环境相对应),它由定义在执行环境中的每个形参值(数组)arguments: [ ]、显式声明的变量(a2: a2)和函数组成,我们无法手动访问它,只有js解析器能访问到它。
6、js解析器,在函数fun2内,创建一个预先包含外部对象(包含全局活动对象和包含自己的对象的活动对象)的作用域链(包括全局活动对象和fun1活动对象),这个作用域链被保存在内部的[[scope]]属性中,我们打印函数时可以看到。
7、js解析器,在函数fun2内,创建一个this对象,未绑定关联值。
执行阶段:执行流进入函数fun1,函数fun1的执行环境(其实就是变量对象)会被推入一个环境栈中,开始执行函数。
1、js解析器,创建一个活动对象(与全局执行环境相对应)。它的初始值,就是预编译时,全局环境的变量对象。
2、js解析器,在函数fun1内,创建一个活动对象(与函数fun1执行环境相对应)。它的初始值,就是预编译时,函数fun环境的变量对象。
3、js解析器,在函数fun1内,复制[[scope]]属性中的对象,构建执行环境的作用域链,并把自己的活动对象推向当前作用域链的前端以此形成完整的作用域链。
4、js解析器,在函数fun1内,给this对象,绑定关联值,即调用该函数的环境对象。
5、js解析器,在函数fun2内,创建一个活动对象(与函数fun2执行环境相对应)。它的初始值,就是预编译时,函数fun环境的变量对象。
6、js解析器,在函数fun2内,复制[[scope]]属性中的对象,构建执行环境的作用域链,并把自己的活动对象推向当前作用域链的前端以此形成完整的作用域链。
7、js解析器,在函数fun2内,给this对象,绑定关联值,即调用该函数的环境对象。
函数fun1执行结束:
1、js解析器,销毁活动对象(与函数fun1执行环境相对应,但活动对象a2没被销毁,因为还在被fun1引用),释放内存。但编译时创建的变量对象未被销毁,还是存在的。
2、闭包被保存在全局变量fun中。
函数fun2执行结束:
1、js解析器,销毁活动对象(与函数fun2执行环境相对应),释放内存。但编译时创建的变量对象未被销毁,还是存在的。变量对象中a2仍旧保持着对fun1中活动对象a2的引用。