声明在任何作用域的变量,都属于这个作用域,仅在作用域内可访问;那变量在作用域内到底是这么编译运行的呢?
变量声明提升
console.log(a);
var a=1;
你觉得以上代码的执行结果是什么呢?大多数会觉得他会报错吧,但其实他会输出 undefined ,这是为什么呢?
这就是我们今天要讲的声明提前,实际上,上一段代码JavaScript编译器会将它拆分成 var a; 和 a=1; 。
第一个定义声明是在编译阶段进行的,第二个赋值声明会留在原有位置等到执行阶段进行。
说一下这段代码打印出来是什么吧?
a=2;
console.log(a);
var a;
你们觉得这会打印出来undefined还是2呢?正确答案是2;实际上这两段代码是这么处理的
//代码块一
var a;
console.log(a);
a=1;
//代码块二
var a;
a=2;
console.log(2);
怎么样,是不是清楚了很多呢?
注意
- 变量声明提升是在所属作用域内,不是整个JavaScript程序内。
- 只是声明提升,赋值或者其他逻辑操作还是保留在原地,不会改变代码的执行顺序。
- ES6中let/const,是不允许先赋值后声明的,它们不能声明提前。
函数声明提升
一样,先上题
foo();
function foo(){
console.log(1);
}
通过变量声明,我想大家很快就能判断出,控制台会输出1,那在看看下边这道呢?
foo();
var foo=function(){
console.log(2)
}
看到这里是不是觉得控制台会输出2,但其实不是,控制台会报错 TypeError:foo is not a function这是为什么呢?
这就要说一下这两种函数定义的区别:
代码块1这种函数定义方式称为函数声明;而代码块2这种函数定义方式称为函数表达式
- 函数声明是将整个函数体提升,但是函数表达式,只是将var foo提升,这时foo并没有赋值为一个函数,所以会报错。
那么在看看这段代码,输出结果
foo(); // TyepError;
fun(); // ReferenceError;
var foo=function fun(){
//...
}
这是由于变量声明提升,但是赋值操作停留在原地;实际这段代码是这样的。
var foo;
foo();
fun();
foo=function(){
var fun= ...self...
}
函数提升优先于变量提升
foo();
var foo;
function foo(){
console.log(1);
}
foo=function(){
console.log(2)
}
大家觉得这个控制台会打印1还是2?实际答案是1,是不是很迷?看看这段代码引擎中是这么样呢?
function foo(){
...}
foo();
foo=function(){
...}
这就需要注意:
- 尽管var foo出现在function foo()之前,但是函数提升优先
- var foo属于重复声明,它会被编译器忽略
- 但是尽管他会被忽略掉,但是出现在后边的函数声明还是可以覆盖前边的函数表达式的。
foo();//1
var foo;
function foo(){
console.log(1);
}
foo=function(){
console.log(2)
}
foo();//2