我们来看下面这个例子:
func();
function func(){
alert("函数被调用了");
}
在上面这段代码中,函数func的调用是在其声明之前,如果说JavaScript代码真的是逐句的解析执行,那么在第一句调用的时候就会出错,然而事实并非如此,上面的代码可以正常执行,并且alert出来"函数被调用了"。
所以,可以得出结论,JavaScript并非仅在运行时简简单单的逐句解析执行!
JavaScript预解析
JavaScript引擎在对JavaScript代码进行解释执行之前,会对JavaScript代码进行预解析,在预解析阶段,会将以关键字var和function开头的语句块提前进行处理。
示例代码:函数名提升
正常函数书写方式
function func(){
alert("函数被调用了");
}
func();
预解析之后,函数名提升
func();
function func(){
alert("函数被调用了");
}
示例代码:变量名提升
正常变量书写方式
alert(a); // undefined
var a = 123;
// 由于JavaScript的预解析机制,上面这段代码,alert出来的值是undefined,
// 如果没有预解析,代码应该会直接报错a is not defined,而不是输出值。
// 变量的时候 提升的只是变量声明的提升,并不包括赋值
var a; // 这里是声明
alert(a); // 变量声明之后并未有初始化和赋值操作,所以这里是 undefined
a = 123; // 这里是赋值
注意:特殊情况
一、函数不能被提升的情况
1函数表达式创建的函数不会提升
test(); // 报错 "test is not a function"
var test = function(){
console.log(123);
}
2new Function创建的函数也不会被提升
test(); // 报错 "test is not a function"
var test = new Function(){
console.log(123);
}
二、出现同名函数
test(); // 打印 '好走的都是下坡路'
// 两个函数重名,这两个函数都会被提升,但是后面的函数会覆盖掉前面的函数
function test(){
console.log('众里寻她千百度,他正在自助烤肉....');
}
function test(){
console.log('好走的都是下坡路');
}
四、函数名与变量名相同
// 如果函数和变量重名,只会提升函数,变量不会被提升
console.log(test); // 打印这个test函数
function test(){
console.log('我是test');
}
var num = 1;
function num () {
console.log(num); // 报错 “num is not a function”
}
num();
//预解析后的代码
function num(){
console.log(num);
}
num = 1;
num();
五、条件式的函数声明
// 如果是条件式的函数申明, 这个函数不会被预解析
test(); // test is not a function
if(true){
function test(){
console.log('只是在人群中多看了我一眼,再也忘不掉我容颜...');
}
}
预解析是分作用域的
声明提升并不是将所有的声明都提升到window 对象下面,提升原则是提升到变量运行的当前作用域中去。
function showMsg(){
var msg = 'This is message';
}
alert(msg); // 报错“Uncaught ReferenceError: msg is not defined”
预解析之后
function showMsg(){
var msg; // 因为函数本身就会产生一个作用域,所以变量声明在提升的时候,只会提升在当前作用域下最前面
msg = 'This is message';
}
alert(msg); // 报错“Uncaught ReferenceError: msg is not defined”
预解析是分段的
分段,其实就分script标签的
<script>
func(); // 输出 AA2;
function func(){
console.log('AA1');
}
function func(){
console.log('AA2');
}
</script>
<script>
function func(){
console.log('AA3');
}
</script>
在上面代码中,第一个script标签中的两个func进行了提升,第二个func覆盖了第一个func,但是第二个script标签中的func并没有覆盖上面的第二个func。所以说预解析是分段的。
tip: 但是要注意,分段只是单纯的针对函数,变量并不会分段预解析。
函数预解析的时候是分段的,但是执行的时候不分段
<script>
//变量预解析是分段的 ,但是函数的执行是不分段
var num1=100;
// test3(); 报错,函数预解析的时候分段,执行的时候才不分段
function test1(){
console.log('我是test1');
}
function test2(){
console.log('我是test2');
}
</script>
<script>
var num2=200;
function test3(){
console.log('test3');
}
test1(); // 打印 '我是test1' 函数执行的时候不分段
console.log(num1); // 100
</script>