在 TypeScript 中,var 还是 var,let 还是 let,const 还是 const,这里简单温习一下 var、let 和 const。
- var
- let
- const
- 解构
- 展开
1. var
使用 var 声明变量并赋值时 var a = 1; 声明 var a 会提升,但赋值 a = 1 不会提升,var 可以重复声明,var 只分函数内和函数外,不存在块级作用域,除了下面 try...catch 块中的这个 e,但其实此处的 e 个人觉得看作是函数的形参或许更好理解:
try {} catch(e) {}
2. let
let 是 ES6 出现的,是块级作用域,不存在变量提升,同一个块内不能重复声明,在声明之前存在暂时性死区,不能进行操作,如下所示,在声明 a 之前就尝试调用函数 foo() 会报错 Cannot access 'a' before initialization:
function foo() {return a}foo()let a
3. const
const 可以理解为 let 的增强,const 声明一个变量的同时要对其进行初始化,并且之后不能直接对声明变量再次赋值,但可以更改变量所指向的对象上的属性:
const a = 18const n // 会报错const a = 25 // 会报错const people = {name: 'xiaofeng',age: a}// 会报错const people = {name: 'xiaoqian',age: a}// 对属性修改是ok的,因为并没有改变对 people 的引用people.age = 25
var、let、const 从最佳实践看来应该优先使用 const,不行再使用 let,而 var 几乎不需要使用,我一时间也想不出在可以写 ES6 的条件下,有什么特殊场景是不能用 let 非要用 var 的。
4. 解构和剩余参数
默认情况下 ts 文件在编译时会被转换为 ES3,看一下就能理解 ES6 语法了,或者到 babel 官网上 Try it out 练练手,看看是如何转换的。
4.1 数组解构
let input = [1, 2]// 数组的解构赋值let [first, second] = input// 等价于// let first = input[0]// let second = input[1]// 解构也能用于函数的参数function foo([first, second]: [number, number]) {console.log(first)console.log(second)}// foo(input) // 使用 ts 编译时会报错,因为参数要求是 Tuple 元祖类型 [number, number],而实际上传入了 number[] 类型// 解决办法是把 input 定义为同样的元祖类型即可let input2: [number, number] = [1, 2]foo(input2) // ok// 解构 + 剩余参数let [first, ...rest] = [1, 2, 3, 4]console.log(first) // 1console.log(rest) // [2, 3, 4]// 还可以使用逗号分隔略过不关心的数据let [, second, , fourth] = [1, 2, 3, 4]console.log(second) // 2console.log(fourth) // 4
4.2 对象解构
解构对象时,要注意如果一个属性值是 null,而不是 undefined,那么不会被默认值赋值。
// 对象解构赋值的同时还可以重命名let o = {a: 'foo',b: 'bar',c: 12,d: false}let { a, b: banana, ...passthrough } = oconsole.log(a) // 'foo'console.log(b) // 报错 Uncaught ReferenceError: b is not definedconsole.log(banana) // 'bar'console.log(passthrough) // {c:12, d: false}// 传入的对象中,b 属性有可能不存在// type C = { a: string, b?: number }function keepWholeObject(wholeObject: { a: string, b?: number }) {// 所以解构时要为 b 属性提供默认值let { a, b = 1000 } = wholeObjectconsole.log(a, b)}// 对象解构时,可以为属性可以提供默认值// 对函数的对象参数进行时提供默认的解构来源function foo({ a, b = 0 } = { a: '' }): void {console.log(a, b)}foo({ a: 'yes' }) // 传入的对象,没有 b 属性,但好在 b 在解构时提供了默认值 0foo() //不传参数时,默认从 { a: '' } 解构foo({}) // 报错!因为类型不匹配。传入空对象,虽然 b 属性有默认值 0,但是 a 属性无从得知
解构表达式有时候会不好理解,尤其和 TS 的类型系统混合在一起时,因此使用时尽量保持小而简单。
5. 展开
5.1 数组的展开
let first = [1, 2]let second = [3, 4]let bothPlus = [0, ...first, ...second, 5]console.log(bothPlus) // [0, 1, 2, 3, 4, 5]
5.2 对象的展开
let defaults = {food: 'apple',price: '$10',total: 50}let search = { ...defaults, food: 'rich' }console.log(search)// {food: "rich", price: "$10", total: 50} 右边的同名属性 food 覆盖了左边展开后的属性 food
通常来说会把默认值放在前边,用于被一些后来设定的属性所覆盖。
