javascript的预编译、作用域和作用域链总结
1、执行环境
也称为执行上下文,当函数在执行时,会创建一个称为执行期上下文的内部对象。一个执行期的上下文定义了一个函数执行时的环境,函数的每次执行时对应的执行上下文都是不一样的,所以多次调用同一个函数会导致创建多个执行上下文。当函数执行完毕,执行上下文被销毁(闭包除外)。它定义了变量和函数有权访问其他数据。
执行环境又分为全局执行环境(GO)和函数执行环境(EO),其中在浏览器中全局执行环境是一个windows对象
2、活动对象和变量对象
活动对象(AO)是执行期上下文的内部变量,每个函数在执行是都会创建一个活动对象,当函数未执行完毕时,又进入到其他作用域中,这个活动对象就变为变量对象(EC),可以理解为当前执行环境的执行期上下文的内部对象称为活动对象,否则称为变量对象。在变量对象和活动对象中都存储了本级环境中定义的变量和函数以及参数。
3、预编译
在进入全局环境和函数环境之前就会进行
预编译步骤
- 1、创建AO对象(执行期上下文),如果全局变量叫做windows对象或GO对象
- 2、变量以及函数提前声明,并且值为underfined
- 3、参数统一,将变量存入各自对象中去
- 4、执行代码
4、作用域链
存储着执行期上下文对象的堆栈这样一个集合称为作用域链,这个集合呈链式链接。在寻找变量的过程,就是从变量作用域链开始查找的,如果在当前作用域中没找到,那么就会去父类的作用域里面找。
作用域链创建过程
- 进入全局执行环境
- 生成全局对象
- 执行代码
- 执行到函数
- 生成活动对象作为变量对象
- 创建作用域链
- 执行函数代码
预编译举例
var a=10, b = 11, c = 12;
function f(a) {
a = 1;
var b = 2;
c = 3;
}
f(10);
console.log(a);
console.log(b);
console.log(c);
- 生成GO对象
GO{ }
- 创建执行期上下文EC(G),查找var变量以及函数声明,并提前
a =und , b=und 11, c=und f=0x0f(一个栈地址)
- 预编译完成,开始从上往下执行代码,给变量赋值。
a =10, b=11, c=12
- 执行f(10),重新创建一个执行上下文EC(f10),创建新的活动变量AO和变量对象EO。
- 执行函数
a =1,b=2,(父类)c=3
因为该函数没有c变量,根据作用域链,要向父类去找,把3赋给父类的c
- 函数运行完毕,由于执行上下文中没有变量指向该堆栈,无法生成闭包,函数销毁
- 打印a、b、c的值
函数销毁,在全局变量中找a、b、c的值,即得:
a=10、b=11、c=3
总结
- js在页面加载过程中顺序执行,但是分块预编译、执行
- js在执行前会进行类似预编译的操作,而且先预先声明变量和定义函数
- 并不是先全文编译完在执行,而是块编译,即一个script块,预编译在执行,然后下一个script块,预编译在执行,但此时上一个预编译的数据都是可用的,但下一个快中的函数,声明的变量不可用
- 变量没用声明就引用,会报错,但对象方法、对象变量没声明,是underfined
- 在函数中变量不声明就赋值,会被认为全局变量,用var声明后为函数变量
- 在执行函数时,也是先编译后执行,但要注意函数定义中的代码即使有错误,只要函数不执行,就不会有影响,但一旦执行函数,开始函数预编译就会出错
补充知识(闭包)
闭包官方解释:
指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。因为定义比较抽象,举一个例子来说明:
function a(){
var i=0;
function b(){
alert(++i);
}
return b;
}
var c=a();
c();
这段代码有两个特点:
- 函数b嵌套在函数a内部
- 函数a返回函数b
这样在执行完var c=a();后变量c实际上指向了函数b,再执行c()后就会弹出一个窗口显示i的值。这段代码其实就创建了一个闭包。
闭包的作用
闭包的作用就是在a执行完并返回后,使得js中垃圾回收机制不会收回a所占用的资源。因为a内部函数b的执行需要依赖a中的变量。
在上面的例子中,由于闭包的存在,使得函数a返回后,a中的i始终存在,这样每次执行c(),都是自加1后输出i的值
那么可以想象另一种情况,如果a返回的不是b,情况完全不同。因为a执行完后,b没有被返回给a的外界,只是被a所引用,而此时a也只会被b引用,两个函数是互相引用又不被外界打扰,函数a、b调用完后就会被回收。
闭包的应用场景
- 保护函数内的变量安全。函数a中的i只有函数b才能访问到,而无法通过其他途径访问
- 在内存中维持一个变量,由于闭包,函数a中的i一直存在于内存中,每次执行调用c();i都会自加1
js的垃圾回收机制
在js中,如果一个对象不再被引用,那么这个对象就会被自动回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。