Var、Let和const命令

Var 变量

ECMAScript 的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。每个变量仅仅是一个用于保存值的占位符而已。定义变量时要使用var操作符(注意var是一个关键字),后跟变量名;

1
var message; // undefined

未经过初始化的变量,会保存一个特殊的——值 undefined ,表示未经初始化

可以在修改变量值的同时修改值的类型

1
2
3
4
var message = 'hi';
typeof message; // 'string'
message = 100; // 有效但不推荐
typeof message; // 'number'

var声明的一个变量将成为定义该变量的作用域中的局部变量,如果在函数内部中使用var声明一个变量,这个变量在函数退出后就会被销毁,例如:

1
2
3
4
5
function test () {
var message = 'hi';
}
test();
console.log(message); // ReferenceError: message is not defined

这里,变量message是在函数中使用var定义的。当函数被调用时,就会创建变量并为其赋值。而在此之后,这个变量又会立即被销毁,因此就会导致错误。

如果在函数内部省略var操作符,会创建一个全局变量:

请看下面例子:

1
2
3
4
5
function test() {
message = 'hi';
}
test();
console.log(message); // 'hi';

这里,省略了var操作符,因而message变成了全局变量,只要调用过一次test()函数,这个变量就有了定义,也就可以在函数外部的任何地方被访问到。

不推荐在局部作用域中定义全局变量,难以维护,如果有意地忽略了var操作符,会由于相应的变量不会马上就有定义而导致不必要的混乱。给未经声明的变量赋值在严格模式下会导致跑出ReferenceError错误

可以使用一条语句定义多个变量(初始化或不初始化均可)用逗号分隔开即可:

例子如下:

1
2
3
var message = 'hi',
found = false,
age = 29;

在严格模式(‘use strict’)下,不能定义名为evalarguments的变量名,否则会报语法错误。

例子如下:

1
2
3
4
5
'use strict'; // Strict Mode 开启严格模式
var eval = 10;
var arguments = 1;
eval; // Uncaught SyntaxError: Unexpected eval or arguments in strict mode
arguments; // Uncaught SyntaxError: Unexpected eval or arguments in strict mode

因为在JavaScript中evalarguments都是字面量


Let 命令

基本用法

ES6中新增了let命令,用于声明变量。用法类似与var,但是所声明的变量只在let命令所在的代码块内有效

如下例子:

1
2
3
4
5
6
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined
b // 1

上面的代码在代码块中分别用letvar声明了两个变量。然后在代码块之外调用这两个变量,结果let声明的变量报错,var声明的变量返回了正确的值。表明,let声明的变量只在其所在的代码块有效。

for循环的计数器,很适合使用let命令

1
2
for (let i = 0; i < arr.lenght; i++) {}
console.log(i); // ReferenceError

以上代码中的计数器i,只在for循环体内有效,所以console.log(i)会报ReferenceError错误

改成下面的代码,如果使用var来声明,最后的结果将输出什么?

1
2
for (var i = 0; i < 10; i++) {};
console.log(i); // 10
1
2
3
4
5
6
7
var a = [];
for (var i = 0; i < 10; i ++ ) {
a[i] = function () {
console.log(i);
};
};
a[6](); // 10

上面的代码中,变量ivar声明的,在全局范围内都有效。所以每一次循环,新的i值都会覆盖旧值,导致最后输出的是最后一轮的i值。

如果使用let,声明的变量仅在块级作用域内有效,最后将输出6

1
2
3
4
5
6
7
var a = [];
for (let i = 0; i < 10; i ++ ) {
a[i] = function () {
console.log(i);
}
}
a[6](); // 6

上面的代码中,变量i是let声明的,当前的i只在本轮循环有效。所以每一次循环的i其实都是一个新的变量,于是最后输出的是6。

不存在变量提升

let不像var

暂时性死区(TDZ / Temporal Dead Zone)

什么是暂时性死区?

  • TDZ 并不是某个地方, 或是内存中的某个区域,而是变量被声明和被初始化之间的这段时间。
1
2
3
4
console.log(aVar); // undefined
console.log(aLet); // ReferenceError: aLet is not defined
var aVar = 1;
let aLet = 2;
1
2
3
4
5
var x = 1;
function foo (x = x) {
console.log(x);
}
foo(); // Uncaught ReferenceError: Cannot access 'x' before initialization

深入理解 Hoisting