JS是一种脚本语言,和一些高级语言不同,它没有完整编译的过程,一般是边写边编译,这也是我们觉得脚本语言比其他语言加载快的原因,JS中有变量声明提升这一机制。
当JS在执行的时候会分为2个阶段,预解析,执行,当JS在执行的时候会将所有用var声明的变量以及关键字定义的函数进行提升( function fn(){......} ),提升到当前作用域的最顶端,而赋值语句在原地等待执行,预解析后,再从上往下逐行解析代码。预解析遵循一些原则,下面一点一点的说明。
提到变量声明提升,就得结合作用域来分析,也就是局部作用域能访问到全局的变量,而全局作用域访问不到局部作用域。
1. 函数声明提升
函数的声明方式
函数声明有两种方式:函数声明,函数表达式(又称函数字面量声明)
- 函数声明提升会在编译阶段把声明和函数体整体都提前到执行环境顶部,所以我们可以在函数声明之前调用这个函数
- 函数表达式,其实就是变量声明的一种,声明关键字会被提升到执行环境顶部,并赋值undefined。赋值语句被留在原地等到执行。
①函数声明的提升
// 函数声明
func()
function func () {
console.log(1);
}
上例不会报错,因为 ‘函数声明提升’,即将函数声明提升(整体)到作用域顶部,实际提升后结果同下:
// 函数声明提升后
function func () {
}
func() // 1
②函数表达式的提升
// 函数表达式
baz();
var baz = function(){
console.log(1);
}
上例会报错输出TypeError: baz is not a function
函数表达式声明提升后,只会把声明操作var baz提升到头部,其他的赋值部分函数体还留在原地,实际提升后结果同下:
//函数表达式的提升后
var baz;
baz(); // TypeError: baz is not a function
baz = function() {
console.log(1);
};
2. 变量声明提升
变量声明提升(只有var声明的变量才有变量提升,let、const无;变量赋值无提升)
变量声明提升会把var声明的头部提升,赋值部分留在原地
1.简单的变量声明提升
console.log(num)
var num = 10
上例输出undefined,变量声明提升会把var num提升,赋值部分num = 10留在原地
// 变量声明提升后
var num
console.log(num)
num = 10
2.函数内的变量声明提升
①举例1
var a=1;
function fn(){
console.log(a);
a=2;
}
fn();
console.log(a);
结果先输出1,后输出2
解析:当fn执行后,会输出1,因为局部作用域能访问到全局作用域,代码逐行执行,向上寻找,找到全局变量a = 1,当再次console.log(a)时,a = 2,修改里全局变量a,所以a = 2(如果函数内部不用var声明a,直接a=2,此时a相当于全局变量)
②举例2
var a = 1;
function fn(){
console.log(a);
var a=2;
}
fn();
console.log(a);
结果先输出undefined,后输出1
解析:当fn执行后,会把函数内部的var声明的变量提升,所以输出undefined,console.log(a)取全局变量a = 1
// 变量提升后
var a = 1;
function fn(){
var a
console.log(a);
a=2;
}
fn(); // undefined
console.log(a); // 1
③举例3
var a=1;
function fn(a){
console.log(a);
a=2;
}
fn(); // undefined
console.log(a); // 1
结果输出又不一样 ,此时执行fn函数,函数传一个参数,就相当于在函数内声明了一个变量,由于fn()没有传参,所以输出undefined,注意此时函数内部的a =2是函数内部变量也就是参数a,不是全局变量。console.log(a),输出全局变量 1
别晕,再看一个例题
④举例4
var a=1;
function fn(a){
alert(a);
a=2;
}
fn(a); // 1
alert(a); // 1
结果又是什么呢?
函数输出1,因为函数调用的时候把全局的a传了进去,即fn(1),所以函数内部a = 1,,全局仍然输出1
3.函数声明与变量声明先后顺序
函数声明会优于变量的声明,也就是说函数会提升到变量的前面
var c = 1;
function d() {
console.log(c);
var c = 2;
}
d();
结果输出 undefined ,函数整体提升到全局变量var c = 1前面,函数内部的var c提升,结果如下
// 提升后
function d() {
var c
console.log(c);
c = 2;
}
var c = 1;
d(); // undefined
4.课后习题
console.log(a);
var a=1;
console.log(a);
function a(){console.log(2)}
console.log(a);
var a=3;
console.log(a)
function a(){console.log(4)}
console.log(a)
a();
第一看到这些代码谁都头大,不过我们来一点一点的看。
从上往下看,遇到var a = 1,提升var a ,a = 1留在原地
var a
console.log(a);
a=1;
console.log(a);
function a(){console.log(2)}
console.log(a);
var a=3;
console.log(a)
function a(){console.log(4)}
console.log(a)
a();
继续往下,遇到函数function a(){console.log(2)},整体提升,把var a替代
function a(){console.log(2)}
console.log(a);
a=1;
console.log(a);
console.log(a);
var a=3;
console.log(a)
function a(){console.log(4)}
console.log(a)
a();
继续往下,遇到函数var a=3,当遇到重名的变量只会留一个,遇到函数,函数的优先级高,所以留函数,继续遇到函数function a(){console.log(4)},提升,替换function a(){console.log(2)}
function a(){console.log(4)}
console.log(a);
a=1;
console.log(a);
console.log(a);
a=3;
console.log(a)
console.log(a)
a();
最后a(),想上找a ,找到a = 3,不是函数报错
输出结果,function a(){console.log(4)} 1 1 3 3 a is not a function
看到这里是不是对变量和函数的提升有了进一步的了解了呢