1. 使用let命令取代var命令
在ES6之前的版本中,使用var
声明变量,在ES6中新增了let
来声明变量。let
完全可以取代var
,因为二者的语义相同,而且let
没有“副作用”。
我们先来看一下let
命令有哪些特点:
- 不存在变量提升:变量只有在声明之后才能使用;
- 不允许重复声明:在相同的作用域下,一个变量名只能使用一次,不能重复声明;
- 具有块级作用域:在代码块中声明的变量,只对当前代码块和其内部嵌套代码块有效,如果嵌套代码块中声明了同名变量时,则对该嵌套代码块无效;
- 存在暂时性死区:当全局变量与代码块中变量同名时,代码块中的变量就有了块级作用域,其变量声明前不能使用。
JavaScript是弱类型的编程语言,在ES6之前使用var
关键字声明变量,会发生变量提升的现象。简单来说,就是在声明变量之前就对该变量进行调用,程序不会报错,而且打印出来的值为undefined
。如果是有Java或C++编程经验的小伙伴,肯定对这种现象是无法忍受的。我们用一段代码来演示一下这种现象:
function test(){
console.log(i);
var i = 1;
}
test();
运行结果:
undefined
上面代码中,变量i
是使用var
命令声明的,我们在变量声明之前在控制台打印i
,虽然此时变量i
还没有声明,但是该变量已经存在了,只不过是没有值,所以会输出undefined
。
如果是同样的代码,把var
替换成let
来声明,其他代码不变,程序运行后的结果为:
当变量使用let
声明时,不会发生变量提升,这就说明在变量声明之前,变量i
是不存在的,要打印一个不存在的变量,就会抛出上面的错误。
在ES5 中有两个作用域,分别是全局作用域和函数作用域,到了ES6就新增了一个块级作用域。如果没有块级作用域的话,在处理业务场景时会带来很多不便,比如内层变量覆盖外层变量,再或者是使用for
循环时,循环中迭代的变量泄露为全局变量等等。
ES6中的let
命令为JavaScript新增了块级作用域,我们看下面的代码:
function test() {
let i = 1;
if(true){
let i = 2;
}
console.log(i);
}
test();
运行结果为:
1
在test()
函数代码块中,对变量i
做了两次声明,按照var
命令声明变量的逻辑,结果应该是2
,但是使用let
命令声明的变量,在该变量的代码块中,不受内层代码块的影响。无论有多少层级的代码块,其每一层都有一个单独的作用域。在内层作用域中可以定义外层作用域的同名变量,但变量的值不受外层作用域变量的影响。
如果理解了let
命令的块级作用域的话,那么再来理解暂时性死区,就轻松多了。我们对上面那段代码稍加改造,演示暂时性死区:
function test() {
let i = 1;
if(true){
console.log(i);
let i = 2;
}
console.log(i);
}
test();
运行结果为:
由于各层级代码块都有自己单独的作用域,内层作用域不受外层的影响,所以当我们在if()
语句中再次声明变量i
时,该作用域下的变量是独立存在的,在未声明之前就调用该变量,就会出现我们前面说过的“变量提升”的概念,let
命令是不存在变量提升的,所以就会抛出上面这种错误。
2. 使用const命令声明常量
const
命令的用法和let
类似,使用const
命令声明的是一个只读常量,一旦声明,常量的值就不能改变。const
声明的变量值不能改变,这就意味着,变量一旦声明后,就必须马上给其赋初始化值。
但是在let
和const
之间,我建议优先使用const
,const
相比let
而言,有几个优点:
一是const
从语义上就表示常量,那就可以提醒协同开发的其他人员,这个变量的值不能被修改,防止误操作修改变量的值导致程序出错;
二是const
比较符合函数式的编程思想,运算不改变值,只是新建值;
三是JavaScript编译器会对const
进行优化,如果const
使用频率比较高的话,有利于提高程序的运行效率,这取决于let
和const
在底层编译器内部的处理差异。
如果从长远的角度看,JavaScript有可能会实现多线程编程,考虑到线程安全,let
命令声明的变量只能在单线程中使用,不能用于多线程的数据共享。