js 预编译 解释执行 作用域链 闭包
<script> var a, b = 1; function c(x){ var aa = 2; function d(){ var ab = 3; } } var d = function(){ //... } c(10); </script>
预编译与解释执行
页面产生时创建全局对象window对象,同时创建document、history、 location、navigator、screen等属性。
脚本文件加载完后,分析语法是否合法。
开始预编译
(1)查找变量声明,作为window属性,并且值为undefined
(2)查找函数声明,作为window属性,值为函数体
上面js预编译后的window对象:
window = { //页面加载创建window时,添加的属性 a: undefined, b: undefined, d: undefined, c: function c(x){ var aa = 2; function d(){ var ab = 3; } } }
预编译后,从上到下解释执行
window.b = 1;
window.d = function(){
//...
}
执行c(10)
解释执行后的window对象:
window = { //页面加载创建window时,添加的属性 a: undefined, b: 1, d: function(){//...}, c: function c(x){ var aa = 2; function d(){ var ab = 3; } } }
作用域与闭包(函数c的创建与执行)
在js文件预编译时,创建c函数,函数c在创建的过程中,创建了一个内部属性[[scope]],即该函数的作用域链,作用域链是作用域对象的集合。
在执行函数c时,首先预编译处理,创建一个执行上下文对象(execute context),即一个函数运行时的环境。执行上下文对象有它自己的作用域链(scope chain)。在上下文对象创建时,初始化它的作用域链。首先把c.scope赋值给context.scope,然后创建一个活动对象(Active Object),并且把活动对象放在context作用域链的最前端。
AO(活动对象)创建:
(1)查找形参和变量声明,赋值为undefined
(2)实参值传给行参
(3)查找函数声明,赋值函数体
在函数执行时,访问数据的权限,从作用域链从上往下查找。
在函数c执行时,函数内部的函数d创建,在创建过程中d有一个自己的作用域链。作用域链指向的对象与c的上下文执行环境的作用域链分别指向相同的对象。
当函数C执行完后,执行上下文对象销毁。由于AO对象被函数d的scope引用,则AO对象不能被销毁。若函数d没有被销毁(如函数d作为返回函数被全局变量引用),AO对象一直存储在内存中。
闭包:一个函数内嵌套一个函数,嵌套的函数未被释放时,外部函数内的变量不能释放。
如:
function A(){
var a = 1;
function B(){
var b = 2;
}
return B;
}
var b = A();
分析:函数A执行后,由于函数内部的嵌套函数被全局变量b引用,即函数B不能被释放。又函数B的作用域链引用了函数A的AO对象,所以函数A内的变量的内存不能被回收。