如果在牛客网刷题的小伙伴,你会发现很多代码执行的结果和你预想的不一致,首先看下面的一段代码
var a=10;
(function test(){
console.log(a)
var a=15;
console.log(a)
})();
上面一段代码,之前一直以为结果是10,15 实际执行结果为
undefined
15
看到结果很意外,问什么第一次打印结果为undefined,这里就要讲到变量提升,任何函数内部变量都会先提升,你可以理解为执行顺序为:
var a=10;
(function test(){
var a;
console.log(a)
a=15;
console.log(a)
})();
看到这个执行顺序是不是感觉很清晰了,变量提升之后,作用域发生了改变,下面我们就来讲讲作用域和变量提升
作用域:
javascript 在es6之前的版本只有全局作用域和函数作用域
全局作用域:在函数任何位置都能访问到,生命周期伴随着页面的生命周期,
函数作用域:是指在函数内部定义的变量和函数,只能在函数内部访问,函数执行之后,函数内部的变量和函数都会被销毁。
es6之后才引入块作用域同时利用let、const来处理var缺陷(变量提升)
一、变量提升:
如果变量声明在函数里面,则将变量声明提升到函数的开头;
如果变量声明是一个全局变量,则将变量声明提升到全局作用域的开头;
变量提升分为两步,第一步变量声明的提升,第二步变量的赋值;
案例1:全局变量提升
console.log(a); // 'undefined'
var a = 'csdn';
console.log(a); // csdn
实际运行时代码:
var a; // 全局变量声明提升
console.log(a); // 'undefined'
a = 'csdn';
console.log(a); // csdn
案例2:函数内变量提升
var a = 'global';
function fun() {
console.log(a); // 'undefined'
var a = 'local';
console.log(a); // local
}
fun();
实际运行时代码:
var a ; // 全局变量声明提升
a ='global';//声明后赋值
function fun() {
var a; //函数内变量声明提升
console.log(a); // 'undefined'
a = 'local';//函数内赋值
console.log(a); // local
}
fun();
二、函数提升
函数的提升是直接将整个函数整体提升到作用域的最开始位置,相当于直接复制到开始;
函数声明只会提升函数声明,不提升函数表达式;
函数提升后会生成一个函数表达式;
案例1:函数声明式
console.log(fun1); // [Function: fun1]
fun1(); // fun1
function fun1() {
console.log('fun1');
}
实际运行时代码
var fun1=function(){
console.log('fun1');
}
console.log(fun1); // [Function: fun1]
fun1(); // fun1
案例2:函数表达式,保持原来不变
console.log(fun2); // undefined
fun2(); // TypeError: fun2 is not a function
var fun2 = function () {
console.log('fun2');
};
实际运行时代码:
console.log(fun2); // undefined
fun2(); // TypeError: fun2 is not a function
var fun2 = function () {
console.log('fun2');
};
三、提升顺序
函数声明提升比变量提升优先级要高,且不会被变量声明覆盖,但会被变量赋值时覆盖;
a() > a
案例:
console.log(a); // f a() {console.log(10)}
console.log(a()); // undefined
var a = 3;
function a() {
console.log(10); //10
}
console.log(a); //3
a = 6;
console.log(a()); //a is not a function;
实际执行结果
var a = function(){ // 函数提升
console.log(10);
}
var a; // 变量提升
console.log(a); // f a() {console.log(10)}
console.log(a()); // undefined
a = 3;
console.log(a); //3
a = 6;
console.log(a()); //a is not a function;
变量提升的意义
我们知道在js运行时需要分两步,一步是解析阶段,一步是执行阶段;
1、容错性更好:
JS作为一个脚本语言,在代码解析阶段,对js进行预编译,就可以发现一些语法上的错误,及时抛出错误,容错性更好;
2、提高性能:
在解析阶段函数的语法检查与预编译,操作只执行一次,目的是为了提升性能,若没有语法解析和预编译,那每次代码执行都要进行预编译和为函数分配空间,这样将要浪费很多时间,占用很多空间,这就是为什么javascript运行速度很快,而Java执行速度很慢,因为每次运行的时候要先预编译。