首先,ES6有一些新的特性,比如说引入了let来定义变量,let和原先的var相比,有一些不同的地方,首先就是let不存在变量提升了,我当时对变量提升有点懵,搞不清楚什么是变量提升,后来才慢慢的搞清楚,变量提升就是把变量声明提升到当前执行环境的最顶端。
先来两段代码比较一下:
var a;
a = 123;
console.log(a);
这个当然非常简单,就是定义了一个变量,给其赋值,然后输出,那看下面这个
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
//相当于
var foo; //声明且初始化为undefined
console.log(foo);
foo=2;
这中情况在函数中也是一样的
function test () {
var a;
console.log(a); //也是打印undefined
a = 123;
}
test();
这个就是典型的变量提升了,在console.log之前,没有变量的定义,但是他的下面有一个var foo=2,这个时候,浏览器的机制会把这些定义赋值的变量都放到最上面去先定义,而且是空的,既然是空的,所以打印的foo当然是undefined,然后打印完了之后才给这个foo赋值,这就是变量的提升,这样简单的情况可能清晰一点,但是放到比较复杂的函数中可能就会混淆了。
下面来看一道题:
console.log(a);
var a = 100;
function foo() {
console.log(a);
var a = 200;
console.log(a);
}
foo();
console.log(a);
输出的结果:
//undefined
//undefined
//200
//100
第一个自然就是变量提升没有赋值打印undefined了,第二个在函数中和函数外的那个一样,在这个函数的作用域中也存在了一个变量提升,所以也是undefined,第三个就是正常打印出函数中的局部变量了,所以是200。最后一个是因为作用域的关系,打印的上面的全局变量,所以是100.
ES6中的let如果也这么写,是不打印undefined的,会直接报错。
console.log(a); // 报错ReferenceError
let a= 2;
//相当于在第一行先声明a但没有初始化,直到赋值时才初始化
let a;
console.log(a);
a = 2;
虽然看起来也是先定义了没赋值,但是在ES6中,console.log的时候,a没有初始化,在a被赋值之前会产生暂时性死区,在这中间是不能对a进行调用的。这也是相较于var,let更优秀的一点,可以避免很多错误,效率也会得到提升。
借别人的话总结一下:我们习惯将var a = 2;看做是一个声明,而实际上javascript引擎并不这么认为。它将var a和a = 2看做是两个单独的声明,第一个是编译阶段的任务,而第二个则是执行阶段的任务。
除了变量可以提升,函数也是可以提升的,函数声明提升就是直接把整个函数提到执行环境的最顶端。而且,在考虑这方面的时候,一定要记住,函数提升要优于变量提升,也就是函数优先原则。
函数声明分为两种:
//函数声明式
function foo () {}
//函数字面量式
var foo = function () {}
函数字面量式的函数提升就类似于变量的函数提升
foo();
var foo = function(){
console.log("aaa");
}
var foo;
console.log(foo); //undefined
foo(); //foo is not a function
foo = function(){
console.log("aaa");
}
这个结果就是undefined,本质还是变量提升
函数声明式有些不同,函数声明式的函数提升就是把函数放到了执行环境的最上面
console.log(foo);
function foo(){
console.log(10);
}
结果就是打印:
ƒ foo(){
console.log(10);
}
因为存在函数提升,把函数放到了最上面,然后再进行变量提升,相当于:
function foo(){
console.log(10);
}
var foo;
console.log(foo);
至于为什么会输出函数,是因为下面的var foo并没有赋值,所以打印的是上面的函数。
如果是
function foo(){
console.log(10);
}
var foo=1;
console.log(foo);
那打印的就是1了。
希望可以对大家理解变量提升和函数提升有帮助。