我们知道javascript定义变量的方式是var,但是var有几个问题。
第一个就是作用域的问题,var不是针对一个块级作用域,而是针对一个函数作用域。举个例子:
function runTowerExperiment(tower, startTime) {
var t = startTime;
tower.on("tick", function () {
... code that uses t ...
});
... more code ...
}
这样是没什么问题的,因为回调函数中可以访问到变量t,但是如果我们在回调函数中再次命名了变量t呢?
function runTowerExperiment(tower, startTime) {
var t = startTime;
tower.on("tick", function () {
... code that uses t ...
if (bowlingBall.altitude() <= 0) {
var t = readTachymeter();
...
}
});
... more code ...
}
后者就会将前者覆盖。
第二个就是循环的问题。
看下面例子:
var messages = ["Meow!", "I'm a talking cat!", "Callbacks are fun!"];
for (var i = 0; i < messages.length; i++) {
setTimeout(function () {
document.write(messages[i]);
},i*1500);
输出结果是:undefined
因为for循环后,i置为3,所以访问不到其值。
为了解决这些问题,ES6提出了let语法。let可以在{},if,for里声明,其用法同var,但是作用域限定在块级。但是javascript中不是没有块级作用域吗?这个我们等会讲。还有一点很重要的就是let定义的变量不存在变量提升。
这里简单提一下什么叫做变量提升。
var v='Hello World';
(function(){
alert(v);
var v='I love you';
})()
上面的代码输出结果为:undefined。
为什么会这样呢?这就是因为变量提升,变量提升就是把变量的声明提升到函数顶部,比如:
(function(){
var a='One';
var b='Two';
var c='Three';
})()
实际上就是:
(function(){
var a,b,c;
a='One';
b='Two';
c='Three';
})()
所以我们刚才的例子实际上是:
var v='Hello World';
(function(){
var v;
alert(v);
v='I love you';
})()
所以就会返回undefined啦。
这也是var的一个问题,而我们使用let就不会出现这个问题。因为它会报语法错误:
{
console.log( a ); // undefined
console.log( b ); // ReferenceError!
var a;
let b;
}
再来看看let的块级作用域。
function getVal(boo) {
if (boo) {
var val = 'red'
// ...
return val
} else {
// 这里可以访问 val
return null
}
// 这里也可以访问 val
}
而使用let后:
function getVal(boo) {
if (boo) {
let val = 'red'
// ...
return val
} else {
// 这里访问不到 val
return null
}
// 这里也访问不到 val
}
同样的在for循环中:
function func(arr) {
for (var i = 0; i < arr.length; i++) {
// i ...
}
// 这里访问得到i
}
使用let后:
function func(arr) {
for (let i = 0; i < arr.length; i++) {
// i ...
}
// 这里访问不到i
}
也就是说,let只能在花括号内部起作用。
再来说说const,const代表一个值的常量索引。
const aa = 11;
alert(aa) //11
aa = 22;
alert(aa) //11
但是常量的值在垃圾回收前永远不能改变,所以需要谨慎使用。
还有一条需要注意的就是和其他语言一样,常量的声明必须赋予初值。即使我们想要一个undefined的常量,也需要声明:
const a = undefined;
最后提一下刚才说到的块级作用域。
在之前,javascript是没有块级作用域的,我们都是通过()来模拟块级作用域。
(function(){
//这里是块级作用域
})();
但是在ES6中,{}就可以直接代码块级作用域。所以{}内的内容是不可以在{}外访问得到的。
我们可以看看如下代码:
if (true) {
function foo() {
document.write( "1" );
}
}
else {
function foo() {
document.write( "2" );
}
}
foo(); // 2
在我们所认识的javascript里,这段代码的输出结果为2。这个叫做函数声明提升,不仅仅提升了函数名,也提升了函数的定义。如果你基础不扎实的话,可以看看这篇文章:深入理解javascript之IIFE
但是在ES6里,这段代码或抛出ReferenceErroe错误。因为{}的块级作用域,导致外面访问不到foo(),也就是说函数声明和let定义变量一样,都被限制在块级作用域中了。