从别人给我的一道题目说起,请问执行下面语句后的输出结果
var a = 3;
function test(){
console.log(a);
a(4);
function a(b){
a = b;
}
console.log(a);
}
console.log(a);
test();
我最初的答案是:
3
3
4
其实应该是
3
function a(b)
4
这道题包含了了函数提升与变量优先级的问题。为了更好地理解这个复合问题,我们一个一个说起。
变量提升#
意思是你代码中声明变量的所在行,其实并不是真正声明的地方,javascript编译时会统一将声明提升到作用域的最上方。
比如当你写下这行代码时:
var a = 3;
编译器会将变量声明提升到代码顶部,但是赋值仍然在原来的地方,变为:
var a;
a = 3;
举个例子:
var a = 3 ;
console.log(a);
console.log(b);
var b = 2 ;
其输出结果为:
3
undefined
因为其实际编译代码为
var a,b;
a = 3;
console.log(a);
console.log(b);
b = 2;
那现在问题升级一下,引入作用域的概念
var a = 3;
function test(){
console.log(a);
a = 4;
console.log(a);
}
其结果为:
3
4
恩,我不知道有没有迷惑到你,反正我被我自己迷惑到了,我们再来下一题
var a = 3;
function test(){
console.log(a);
var a = 4;
console.log(a);
}
test();
其结果为:
undefined
4
这里要先说一下同名变量优先级,在同一作用域内,两个同名变量中,局部变量的优先级 > 全局变量的优先级,即test方法中的a会覆盖外部的a。
再回到变量提升,所以test方法在编译后实际是这样的
function test(){
var a ; // 变量声明提升到方法顶部
console.log(a);
a = 4; // 赋值仍然在原处进行
console.log(a);
}
变量作用域#
所有不用var声明的变量都是全局变量
在函数体内,同名的局部变量或参数优先级会高于同名局部变量,即
var a = 3;
function test() {
var a = 4;
console.log(a);
}
test();
console.log(a);
会输出
4
3
函数提升#
变量提升理解了,函数提升会好理解一些,函数的声明会提到作用域的顶端,但不同的是,函数提升会将其方法体的内容一起提升,举例来说
function test(){
test1();
var b = 5;
function test1(){
console.log(b);
}
}
test();
会被编译为
function test(){
var b;
function test1(){
console.log(b);
}
test1();
b = 5;
}
test();
输出
undefined
函数声明方式分为两种,函数声明(函数式声明)和函数表达式(变量式声明),<b>只有函数声明才会执行函数提升</b>
console.log(a);
console.log(b);
// 函数声明 执行函数提升
function a(){}
// 函数表达式 不执行函数提升 但可以看成是变量提升
var b = function(){}
输出
function a()
undefined
最后,再看下这个问题
var a;
function a(){
console.log(2);
}
console.log(a);
结果到底会是undefined还是function?输出如下
function a()
那再加强一下
var a;
function a(){
console.log(2);
}
console.log(a);
a();
a = 3;
console.log(a);
输出
function a()
2
3
函数提升优先级比变量提升要高,且不会被变量声明覆盖,但是会被变量赋值覆盖。现在应该明白最上方那道题为什么会是这个结果了吧?其编译过程如下:
var a;
function test() {
function a(b) {
a = b;
}
console.log(a); // functing a(b)
a(4);
console.log(a); // 4
}
a = 3;
console.log(a); // 3
test();