预编译
在了解预编译之前,要先了解函数的声明和赋值
function test(){}//这种写法是函数的声明
var test = function(){}//这种写法相当于给函数赋值
在程序执行之前,会将所有用var声明的变量或者用function声明的函数提到当前作用域的最前面,赋值留在原地;当前作用域有可能是全局作用域,也有可能是局部作用域(全局作用域指window,局部作用域指一般只在固定的代码片段内可访问到,最常用的就是函数的作用域);对于变量,任何没有声明就赋值的变量,最后都会被作为全局变量,即使这个变量是写在函数中的,但这个写在函数中的全局变量依旧受到函数的约束,如果函数没有执行,那么在函数体外要用到这个变量的代码会被报错;如:
function test(){
a = 100;
}
console.log(a);
test()
函数的预编译可分为四个步骤:
function test(c){
console.log(a)//undefined 因为局部的变量a还未声明
console.log(c)//ƒ c(){}
a = 100;//未被声明的全局变量a
var a = 20;//声明函数的局部变量a
function c(){}
console.log(b)//ƒ b(){} 函数的声明在执行之前已经被提到最前,所以可以找到
function b(){}
console.log(a);//20
}
test(5)
以上面的函数为例
步骤1: 先创建活跃对象(AO Activation Object)
AO{
}
步骤2:找出函数作用域内的所有变量以及形参,将变量名或形参作为属性值,目前undefined为属性值
AO{
c:undefined,
a:undefined,
b:undefined
}
步骤3:将形参和实参统一
AO{
c:5,
a:undefined,
b:undefined
}
步骤4:在函数内找到变量的声明,并将其赋值给变量
AO{
c:5,
a:20,
b:fn b()
}
作用域链
当声明一个函数时,局部作用域一级一级向上包起来,就是作用域链。
var a = 5;
function test(){
function fun(){
var a = 10;
console.log(a)//10
function fn(){
console.log(a)//10
}
fn()
}
fun()
console.log(a)//5
}
test()
例如以上代码,执行函数时,总是先从自己内部寻找变量;如果找不到想要的变量,就会从声明这个函数的作用域寻找想要的变量;