ES6建议不再使用var
定义变量,而使用let
定义变量,const
定义常量。
无论是
let
还是const
,它们均解决了JS中长久以来的变量定义的问题。
咱们先聊聊var
和let
的区别。
一、变量具有块级作用域,在代码块之外不可使用
使用var
定义变量时,无论其实际声明位置在何处,都会被视为声明于所在函数的顶部 (如果声明不在任意函数内,则视为在全局作用域的顶部),使用if或者for等循环定义的变量,变量同样会被提升到if或for所在的函数顶部。
使用let
定义变量时,该变量只能在当前块级作用域里使用,在外部访问会显示未定义
块级作用域(又被称为词法作用域)会在以下两种情况被创建:
- 在一个函数内部
- 在一个代码块(由一对花括号包裹,比如if、for、switch、while)内部
接下来我们看看在循环中使用的情况
如果我们在for循环中用var
定义变量,在延迟后输出i
,可以看到全部输出的都是3,这是由于i
变量的定义被提升出去了,左边的代码等价于右边的代码
而在过去我们要解决这个问题只能通过 IIFE 来强制改变作用域去解决这个问题
如果在for循环中使用let
定义变量,变量所在的作用域是在循环体这个代码块内,因此在循环外就不能使用了。另外,for循环会对该变量做特殊处理,让每次循环使用的都是一个独立的循环变量。
二、变量的提升问题
用var
定义的变量先赋值再声明是没有任何问题的,因为里面会有变量提升的情况。
而用let
定义的变量不会被提升,这里有个暂时性死区的概念
暂时性死区
使用
let
或const
声明的变量,在到达声明处之前都是无法访问的,会被放置在JS社区称为暂时性死区( temporal dead zone ,TDZ )的区域内,试图访问会导致一个引用错误。虽然该名称并未在 ECMAScript 规范中被明确命名,但经常被用于描述
let
或const
声明的变量为何在声明处之前无法被访问。
下面放一个先赋值再声明会直接报 Cannot access 'a' before initialization 的错误的例子
三、全局定义的变量不再作为属性添加到全局对象中
在全局使用var
定义的变量会被挂载到全局也就是window对象中
而使用let
定义的变量并不会被挂载到全局对象中
四、不可重复定义同名变量
var
在重复定义同名变量的时候并不会报错,最终打印会是最后一个所赋的值
let
重复定义同名变量会直接报 Identifier 'a' has already been declared 的错误
下面来聊聊const
的使用
const
和let
同样具有块级作用域,且声明不会被提升的特点。- 使用
const
定义变量时,必须初始化
在过去只有var
一个关键字,没有办法清晰的表明这个变量是否需要修改,而新增了let
和const
关键字后,可以使用let
来声明可以被修改的变量,使用const
来声明不会被修改的变量,而使用const
声明的变量会被认为是常量( constant ),这样语义上会更加清晰。
如果我们尝试修改const
定义的变量,会报Assignment to constant variable错误。
由于不可以被更改的特性,那么我们用const
定义变量的时候就必须初始化,不然会报Missing initializer in const declaration的错误,而使用var
和let
的话,可以先声明后面需要的时候再赋值。