var和let、const对比

简介


ECMAScript6中新增两个变量声明的指令letconst,以前经常用的var有什么区别。

var声明


var 声明语句声明一个变量,并可选地将其初始化为一个值。

  • 可重复声明
  • 变量提升
  • 声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的。
1
var count = 1;

通过var关键字声明一个count变量,并且count的值为1。

可重复声明

可以重复声明count变量,也可以重新赋值,代码如下:

1
2
3
4
var count = 1;
var count = 2;
count = 3;
console.log(count); // 3

变量提升

var声明的变量是存在变量提升的 ,由于变量声明(以及其他声明)总是在任意代码执行之前处理的,所以在代码中的任意位置声明变量总是等效于在代码开头声明。代码如下:

1
2
console.log(count); // undefined
var count = 1;

提升将影响变量声明,而不会影响其值的初始化

作用域

var声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的。代码如下:

1
2
3
4
5
6
7
8
function x() {
var count = 1;
sum = 2;
}
x();
console.log(count); // undefined
console.log(sum); // 2
console.log(window.sum); // 2
  • 建议始终声明变量,无论它们是在函数还是全局作用域内。

let声明


let允许你声明一个作用域被限制在 块级中的变量语句或者表达式。与 var 关键字不同的是, var声明的变量只能是全局或者整个函数块的。

  • 不可重复声明,可以重复赋值
  • 块作用域
  • 暂存死区
  • 不存在变量提升

不可重复声明,可以重复赋值

let不可以重复同样名称的变量,但是可以重复给同一个变量多次赋值。代码如下:

1
2
3
4
let count = 1;
let count = 2; // Identifier 'count' has already been declared
count = 2;
console.log(count); // 2

块作用域

let声明的变量只在其声明的块或子块中可用,这一点,与var相似。二者之间最主要的区别在于var声明的变量的作用域是整个封闭函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function varTest() {
var x = 1;
if (true) {
var x = 2; // 同样的变量!
console.log(x); // 2
}
console.log(x); // 2
}

function letTest() {
let x = 1;
if (true) {
let x = 2; // 不同的变量
console.log(x); // 2
}
console.log(x); // 1
}

let声明的变量不会挂载顶层对象下面,会临时创建一个scope来储存let声明,执行完成清除,示例代码如下:

1
2
3
4
5
<script>
debugger
let count = 1;
var sum = 2;
</script>

在执行完let count = 1;,效果如下图所示:
let
javascript执行完成后,scope也会被清空。

暂存死区/不存在变量提升

let 被创建在包含该声明的(块)作用域顶部,一般被称为“提升”。与通过 var 声明的有初始化值 undefined 的变量不同,通过 let 声明的变量直到它们的定义被执行时才初始化。在变量初始化前访问该变量会导致 ReferenceError。该变量处在一个自块顶部到初始化处理的“暂存死区”中。
代码如下:

1
2
3
4
console.log(count); // Uncaught ReferenceError: Cannot access 'count' before initialization
console.log(sum); // undefined
let count = 1;
var sum = 2;

const声明


常量是块级作用域,很像使用 let 语句定义的变量。常量的值不能通过重新赋值来改变,并且不能重新声明。

  • 不能重复声明
  • 不能重复赋值
  • 块级作用域
  • 暂存死区
  • 不存在变量提升
  • 一旦声明,必须马上赋值

不能重复声明块级作用域块级作用域不存在变量提升在这里不再赘述和let中的表现相同,请看上文。

一旦声明,必须马上赋值

const 声明之后必须马上赋值,否则会报错,代码如下:

1
2
const count; // Uncaught SyntaxError: Missing initializer in const declaration
const sum = 2;

不能重复赋值

const声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。
JavaScript中的数据类型分为两大类:值类型引用类型

值类型

const声明的变量被首次被赋值为值类型,它的值就不能改变了,代码如下:

1
2
const count = 1;
count = 2; // Uncaught TypeError: Assignment to constant variable.

引用类型

只要不改变量的指针地址就不会报错,代码如下:

1
2
3
4
5
const user = {
name: 'nihao',
age: 17
};
user.name = '大家好';

总结

总结对比如下面表格所示:

/ 能否重复声明 能否重复赋值 作用域 变量提升 暂存死区 声明是否需要立即赋值
var 函数作用域/全局作用域 存在变量提升 不存在暂存死区 不需要立即赋值
let 不能 块级作用域/全局作用域 不存在 存在暂存死区 不需要立即赋值
const 不能 不能 块级作用域/全局作用域 不存在 存在暂存死区 需要立即赋值

他们的特性基本上如上面表格所示,可以根据各个不同的需要,选择var、let、const来声明变量。

JavaScript类型转换(一) 常见数据类型

参考

var 声明语句
let 声明语句
const 声明语句
JS 系列一:var、let、const、解构、展开、函数
ES6中let、const与var的区别