1. 解释
TypeScript 是 JavaScript 的超集,同 JavaScript 一样,声明变量可以采用下面三个关键字:
- var
- let
- const
2. var 声明
通过 var
关键字来定义 JavaScript 变量,这个大家都能理解:
- 1
2.1 作用域
下面我们来讨论一个为什么尽量避免使用 var
。
快速的猜一下下面的代码会返回什么:
相信很多人都踩过这个坑,这几乎是作用域的必考题目,但如果你不了解,不要担心,你并不是一个人。大多数人期望输出的结果是这样的:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
然而,实际结果是这样的:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
解释:这里的 i
是在全局作用域中的,只存在一个值。 setTimeout
这个异步函数在若干毫秒后执行,并且它是在 for
循环结束后的。在 for
循环结束后, i
的值为 10
,所以当函数被调用的时候,它会打印出 10
。
下面介绍几种可以实现我们预期输出结果的方法:
- 通过调用函数,创建函数作用域:
- 立即执行函数,创建函数作用域:
- 通过
let
关键字创建块级作用域:
2.2 变量提升
看下面的例子:
- 1
- 2
它等价于:
- 1
- 2
- 3
从上面的例子中可以看出,关键词 var
会进行变量提升。
如果把上面的 var
换成 let
呢?
- 1
- 2
会报错 a is not defined
未定义,这里也可以看出 var
与 let
在变量提升的不同:
- var 会将变量的创建和初始化都进行提升
- let 只会将创建提升,而初始化未被提升,称之为暂时性死区
3. let 声明
现在你已经知道了 var
存在全局作用域和变量提升的问题,这恰好说明了为什么用 let
语句来声明变量。
- 1
下面介绍 let
的一些特点:
3.1 块作用域
在 ES6 之前,ECMAScript 的作用域只有两种:
- 全局作用域
- 函数作用域
现在通过命令 let 和 const 新增了“块级作用域”,定义在代码块中的变量在代码块被执行结束后会被释放掉:
代码解释:
第 3 - 4 行,a 变量在 if{} 代码块中是有效的,正常输出 10。
第7行,在 if{} 代码块外就被释放掉了,所以会报错误。
包括在一对花括号中的一组语句称之为”代码块“,它可以替换掉上面介绍 var 时使用的立即执行函数:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
3.2 重定义
在使用 var
声明时,不论声明几次,最终只会得到一个:
- 1
- 2
- 3
这是一个完全有效的代码,所有 x
的声明其实都指向了同一个引用,但这也是很多 bug 产生的原因。 let
的声明就严格了许多:
- 1
- 2
代码解释: 第 2 行,重新声明了块作用域的变量 x 。
4. const 声明
关键字 const
声明变量,它被赋值后不能再改变。 换句话说,它拥有与 let
相同的作用域规则,但是不能重新赋值。
用 const 声明变量,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。用 const 声明初始数据类型如布尔值、数字、字符串,可以理解为声明常量,因为这些初始类型的值就保存在变量所指向的那个内存地址。
- 1
- 2
- 3
- 4
- 5
代码解释: 第 5 行,在给 num 第二次赋值时会报错。
对于复合类型的数据来说,变量所指向的内存地址保存的只是一个指针,const 能够保证其指针不变,但属性值是可变的:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
代码解释: 第 7 行,对已经使用 const 声明的变量重新赋值报错,但是在第 13 行,只是对这个对象的属性赋值是可以的。
5. 小结
阅读本小节中三种不同的变量声明,我们知道了:
let
和count
实现了块级作用域。- 所有变量除了你计划去修改的都应该使用
const
。 - 尽量使用
let
和const
来声明变量,减少var
的使用。