JavaScript中的变量声明目前有三种:var、let、const。let和const是JavaScript里相对较新的变量声明方式。 let在很多方面与var是相似的,但是可以帮助大家避免在JavaScript里常见一些问题。 const是对let的一个增强,它能阻止对一个变量再次赋值。
var
- 奇怪的作用域规则
function f(shouldInitialize) {
if (shouldInitialize) {
var x = 10;
}
return x;
}
f(true); // returns '10'
f(false); // returns 'undefined'
变量 x是定义在if语句里面,但是我们却可以在语句的外面访问它。 这是因为 var声明可以在包含它的函数,模块,命名空间或全局作用域内部任何位置被访问,包含它的代码块对此没有什么影响。 有些人称此为var作用域或函数作用域。 函数参数也使用函数作用域。
- 捕获变量的怪异之处
for (var i = 0; i < 10; i++) {
setTimeout(function() { console.log(i); }, 100 * i);
}
输出 >>
10
10
10
10
10
10
10
10
10
10
相信大部分时候我们的期望输出是
0
1
2
3
4
5
6
7
8
9
原因:console输出的 i与for循环中的 i是同一个作用域里头的同一个 i,setTimeout后执行console.log时 i已经结束了循环,值已然是10了。
通常的一个解决办法是采用立即执行函数表达式(IIFE)来捕获每次循环时候的 i
for (var i = 0; i < 10; i++) {
(function(i){
setTimeout(function() { console.log(i); }, 100 * i)
})(i);
}
let
- 块作用域
function f(input) {
let a = 100;
if (input) {
// Still okay to reference 'a'
let b = a + 1;
return b;
}
// Uncaught ReferenceError: b is not defined
return b;
}
a的作用域是f函数内,b的作用域是if语句块内
a++; // Uncaught ReferenceError: a is not defined
let a;
拥有块级作用域的另一个显著特点就是:不能先调用再定义,因为不会像var一样做变量提升
function foo() {
// okay to capture 'a'
return a;
}
// 不能在'a'被声明前调用'foo'
// 运行时应该抛出错误
foo();
let a;
注意:我们仍然可以在一个拥有块作用域变量被声明前获取它。 只是我们不能在变量声明前去调用那个函数。
如果改成下面的代码,就没毛病 >>
function foo() {
// okay to capture 'a'
return a;
}
let a;
foo();
- 重定义及屏蔽
let x = 10;
let x = 20; //Uncaught SyntaxError: Identifier 'x' has already been declared
在一个嵌套作用域里引入一个新名字的行为称做屏蔽。 它是一把双刃剑,它可能会不小心地引入新问题,同时也可能会解决一些错误。
function sumMatrix(matrix) {
let sum = 0;
for (let i = 0; i < matrix.length; i++) {
var currentRow = matrix[i];
for (let i = 0; i < currentRow.length; i++) {
sum += currentRow[i];
}
}
return sum;
}
这中写法的循环能得到正确的结果,因为内层循环的i可以屏蔽掉外层循环的i。
- 块级作用域变量的获取
let声明出现在循环体里时拥有完全不同的行为。 不仅是在循环里引入了一个新的变量环境,而是针对 每次迭代都会创建这样一个新作用域。 这就是我们在使用立即执行的函数表达式时做的事,所以在 setTimeout例子里我们仅使用let声明就可以了。
for (let i = 0; i < 10; i++) {
setTimeout(function() { console.log(i); }, 100 * i);
}
const
与let声明相似,但是就像它的名字所表达的,被赋值后不能再改变。
const obj = {name: 'qingqinxl', age: 18};
obj = {name:'Lily', age:18}; //Uncaught TypeError: Assignment to constant variable.
obj内部状态可以被修改
obj.name = 'Lily';
obj.age = obj.age + 1;
// obj
// {name: "Lily", age: 19}
let vs const
所有变量除了你计划去修改的都应该使用const。