js这门语言的特点是解释性语言,解释一行执行一行,逐行执行,预编译发生在代码执行之前,上下文创建之后的一段时期,在这个时期会对js代码进行预处理。分为全局预编译和函数预编译
全局预编译
全局上下文创建后,会生成变量对象VO(Variable Object)
-
VO首先寻找变量声明,将var声明的变量作为VO对象的属性名,值为undefined
-
然后寻找函数声明,属性值为函数本身
-
如果函数名与变量名冲突,函数声明会将变量声明覆盖。
console.log(a)//function
var a = 100;
function a () { }
console.log(a)//100
执行顺序:变量声明—>函数声明
函数预编译
函数上下文创建后,会生成变量对象AO(活动对象) (函数由于执行才被激活 称为 Active Object)
-
寻找变量声明, 变量名作为AO对象的属性名, 属性值置为 undefined
-
寻找形参, 形参名作为AO对象的属性名, 属性值置为 undefined
-
将实参的值赋予形参, 即替换 AO对象中形参的属性值
-
寻找函数声明, 函数名作为AO对象的属性名, 属性值为函数本身
-
如果函数名与变量名冲突, 函数声明会将变量声明覆盖
function a(b, c) {
console.log(b); //1
var b = 0
console.log(b); //0
var b = function () {
console.log('bbbb')
}
console.log(c); //undefined
console.log(b); //function
}
a(1)
执行顺序:变量声明—>形参—>实参—>函数声明
常见面试题(实际演练)
function fn (a, c) {
console.log(a)//function
var a = 123
console.log(a)//123
console.log(c)//function
function a() {}
if (false) {
var d = 678
}
console.log(d)//undefined
console.log(b)//undefined
var b = function () {}
console.log(b)//function
function c () {}
console.log(c)//function
}
fn(1, 2)
所有var声明的变量会声明提前,赋值为undefined,覆盖了全局的foo
var foo = 1;
function bar() {
//由于var变量声明提前,这相当于有一个var foo,但是没有赋值
console.log(foo); //undefined
if (!foo) {
var foo = 10; //相当于 foo = 10;
}
console.log(foo); //10
}
bar();
-
使用函数声明定义的函数会声明提前,可以随意在函数前后调用
-
函数表达式不能执行的原因是:var声明提前,但是赋值undefined,undefined()会报错
function fn () {
func() //声明式
//函数表达式 这里会覆盖提前的函数声明
var func = function () {
console.log('表达式')
}
//函数声明
function func() {
console.log('声明式')
}
func() //表达式 原因:函数表达式将函数声明覆盖了
}
fn()
function test(d) {
console.log(b);
if (a) {
b = 100;
}
console.log(b);
c = 4;
console.log(d);//undefined
var d = 20;
console.log(d);//20
}
var a = 10;
var b = 10;
test(3);
console.log(c);