var/let/const声明,解构,展开快速入门

let 声明 const 声明(被赋值后不能再改变)

let hello = "Hello!";

1.块作用域:a.块作用域变量在包含它们的块或for循环之外是不能访问的 b.不能在被声明之前读或写

2.重定义及屏蔽: 不能在1个作用域里多次声明`x`

解构

解构数组

let input = [1, 2];

let [first, second] = input;

console.log(first); // outputs 1

console.log(second); // outputs 2

对象解构

let o = { a: "foo", b: 12, c: "bar" };

let { a, b } = o;

属性重命名

let { a: newName1, b: newName2 } = o;

默认值

function keepWholeObject(wholeObject: { a: string, b?: number }) {

let { a, b = 1001 } = wholeObject;

}

函数声明

type C = { a: string, b?: number }
function f({ a, b }: C): void {// ...
}

展开

展开数组

let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5];

展开对象

let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" };

-----------------------------------------------------------------------------------------------------------------

# 变量声明

`let` 和 `const` 是 JavaScript 里相对较新的变量声明方式。`let` 在很多方面与 `var` 是相似的,但是可以帮助大家避免在 JavaScript 里常见一些问题。`const` 是对 `let` 的一个增强,它能阻止对一个变量再次赋值。

因为 TypeScript 是 JavaScript 的超集,所以它本身就支持 `let` 和 `const`。 下面我们会详细说明这些新的声明方式以及为什么推荐使用它们来代替 `var`。

如果你已经对 `var` 声明的怪异之处了如指掌,那么你可以轻松地略过这节。

1.var声明

## var 声明在 ES5 的时代,我们都是通过 `var` 关键字定义JavaScript 变量:```javascript
var a = 10
```大家都能理解,这里定义了一个名为 `a` 值为 `10` 的变量。我们也可以在函数内部定义变量:```javascript
function f() {var message = 'Hello World!'return message
}
```并且我们也可以在其它函数内部访问相同的变量:```javascript
function f() {var a = 10return function g() {var b = a + 1return b}
}var g = f()
g() // returns 11
```上面的例子是一个典型的闭包场景,`g` 可以获取到 `f` 函数里定义的 `a` 变量。 每当 `g` 被调用时,它都可以访问到 `f` 里的 `a` 变量。 即使当 `g` 在 `f` 已经执行完后才被调用,它仍然可以访问 `a`。### 作用域规则`var` 声明有些奇怪的作用域规则。 看下面的例子:```javascript
function f(shouldInitialize) {if (shouldInitialize) {var x = 10}return x
}f(true)  // returns '10'
f(false) // returns 'undefined'
```有些同学可能要多看几遍这个例子。 变量 `x` 是定义在 `if` 语句里面,但是我们却可以在语句的外面访问它。 这是因为 `var` 声明的作用域是函数作用域,函数参数也使用函数作用域。这些作用域规则可能会引发一些错误。 其中之一就是,多次声明同一个变量并不会报错:```javascript
function sumMatrix(matrix) {var sum = 0for (var i = 0; i < matrix.length; i++) {var currentRow = matrix[i]for (var i = 0; i < currentRow.length; i++) {sum += currentRow[i]}}return sum
}
```这里很容易看出一些问题,里层的 `for` 循环会覆盖变量 `i`,因为所有 `i` 都引用相同的函数作用域内的变量。 有经验的开发者们很清楚,这些问题可能在代码审查时漏掉,引发无穷的麻烦。### 捕获变量怪异之处猜一下下面的代码会返回什么:```javascript
for (var i = 0; i < 10; i++) {setTimeout(function() {console.log(i)}, 100 * i)
}
```答案是,`setTimeout` 会在若干毫秒的延时后执行一个函数(等待其它代码执行完毕):``` javascript
10
10
10
10
10
10
10
10
10
10
```很多 JavaScript 程序员对这种行为已经很熟悉了,但如果你很不解也没有关系,因为你并不是一个人。 大多数人期望输出结果是这样:```javascript
0
1
2
3
4
5
6
7
8
9
```> 我们传给 `setTimeout` 的每一个函数表达式实际上都引用了相同作用域里的同一个 `i`。让我们花点时间思考一下这是为什么。 `setTimeout` 在若干毫秒后执行一个函数,并且是在 `for` 循环结束后。`for` 循环结束后,`i` 的值为 `10`。 所以当函数被调用的时候,它会打印出 `10`。一个通常的解决方法是使用立即执行的函数表达式(IIFE)来捕获每次迭代时 `i` 的值:```javascript
for (var i = 0; i < 10; i++) {(function(i) {setTimeout(function() {console.log(i)}, 100 * i)})(i)
}
```这种奇怪的形式我们已经司空见惯了。 参数 `i` 会覆盖 `for` 循环里的 `i`,但是因为我们起了同样的名字,所以我们不用怎么改 `for` 循环体里的代码。

2.let声明

## let 声明现在你已经知道了 `var` 存在一些问题,这恰好说明了为什么用 `let` 语句来声明变量。 除了名字不同外, `let` 与 `var` 的写法一致:```javascript
let hello = 'Hello!'
```主要的区别不在语法上,而是语义,我们接下来会深入研究。### 块作用域当用 `let` 声明一个变量,它使用的是块作用域。 不同于使用 `var` 声明的变量那样可以在包含它们的函数外访问,块作用域变量在包含它们的块或 `for` 循环之外是不能访问的。```typescript
function f(input: boolean) {let a = 100if (input) {// OK: 仍然能访问到 alet b = a + 1return b}// Error: 'b' 在这里不存在return b
}
```这里我们定义了 2 个变量 `a` 和 `b`。 `a` 的作用域是 `f` 函数体内,而 `b` 的作用域是 `if` 语句块里。在 `catch` 语句里声明的变量也具有同样的作用域规则。```typescript
try {throw 'Oh no!';
}
catch (e) {console.log('Catch it.')
}// Error: 'e' 在这里不存在
console.log(e)
```拥有块级作用域的变量的另一个特点是,它们不能在被声明之前读或写。 虽然这些变量始终“存在”于它们的作用域里,但在直到声明它的代码之前的区域都属于*暂时性死区*。 它只是用来说明我们不能在 `let` 语句之前访问它们,幸运的是 `TypeScript` 可以告诉我们这些信息。```typescript
a++ // TS2448: Block-scoped variable 'a' used before its declaration.
let a
```注意一点,我们仍然可以在一个拥有块作用域变量被声明前获取它。 只是我们不能在变量声明前去调用那个函数。 如果生成代码目标为 ES2015,现代的运行时会抛出一个错误;然而,现今 TypeScript 是不会报错的。```typescript
function foo() {// okay to capture 'a'return a
}// 不能在'a'被声明前调用'foo'
// 运行时应该抛出错误
foo()let a
```关于*暂时性死区*的更多信息,查看这里 [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone_and_errors_with_let)。### 重定义及屏蔽我们提过使用 `var` 声明时,它不在乎你声明多少次;你只会得到 1 个。```javascript
function f(x) {var xvar xif (true) {var x}
}
```在上面的例子里,所有 `x` 的声明实际上都引用一个相同的`x`,并且这是完全有效的代码,但这经常会成为 `bug` 的来源。幸运的是 `let` 的声明就不会这么宽松了。```typescript
let x = 10
let x = 20 // 错误,不能在 1 个作用域里多次声明 x
```并不是要求两个均是块级作用域的声明 TypeScript 才会给出一个错误的警告。```typescript
function f(x) {let x = 100 // Error: 干扰参数声明
}function g() {let x = 100var x = 100 // Error: 不能同时具有 x 的两个声明
}
```并不是说块级作用域变量不能用函数作用域变量来声明。 而是块级作用域变量需要在明显不同的块里声明。```typescript
function f(condition, x) {if (condition) {let x = 100return x}return x
}f(false, 0) // returns 0
f(true, 0)  // returns 100
```在一个嵌套作用域里引入一个新名字的行为称做屏蔽。 它是一把双刃剑,它可能会不小心地引入新问题,同时也可能会解决一些错误。 例如,假设我们现在用 `let` 重写之前的 `sumMatrix` 函数。```typescript
function sumMatrix(matrix: number[][]) {let sum = 0for (let i = 0; i < matrix.length; i++) {let currentRow = matrix[i]for (let i = 0; i < currentRow.length; i++) {sum += currentRow[i]}}return sum
}let matrix = [[1,2,3],[4,5,6]
]console.log(sumMatrix(matrix))  //21
```这个版本的循环能得到正确的结果,因为内层循环的 `i` 可以屏蔽掉外层循环的 `i`。通常来讲应该避免使用屏蔽,因为我们需要写出清晰的代码。 同时也有些场景适合利用它,你需要好好权衡一下。### 块级作用域变量的获取每次进入一个作用域时,`let` 会创建一个变量的环境。就算作用域内代码已经执行完毕,这个环境与其捕获的变量依然存在。回想一下前面 `setTimeout` 的例子,我们最后需要使用立即执行的函数表达式来获取每次 `for` 循环迭代里的状态。 实际上,我们做的是为获取到的变量创建了一个新的变量环境。 这样做挺痛苦的,但是幸运的是,你不必在 `TypeScript` 里这样做了。当 `let` 声明出现在循环体里时拥有完全不同的行为。不仅是在循环里引入了一个新的变量环境,而且针对每次迭代都会创建这样一个新作用域,这就相当于我们在使用立即执行的函数表达式时做的事。所以在 `setTimeout` 例子里我们仅使用 `let` 声明就可以了。```typescript
for (let i = 0; i < 10 ; i++) {setTimeout(function() {console.log(i)}, 100 * i)
}
```会输出与预料一致的结果:```javascript
0
1
2
3
4
5
6
7
8
9
```

3.const声明

## const 声明`const` 声明是声明变量的另一种方式。```typescript
const numLivesForCat = 9
```它们与 `let` 声明相似,但是就像它的名字所表达的,它们被赋值后不能再改变。 换句话说,它们拥有与 `let` 相同的作用域规则,但是不能对它们重新赋值。这很好理解,它们引用的值是不可变的。```typescript
const numLivesForCat = 9
const kitty = {name: 'Kitty',numLives: numLivesForCat
}// Error
kitty = {name: 'Tommy',numLives: numLivesForCat
};// OK
kitty.name = 'Jerry'
kitty.numLives--
```除非你使用特殊的方法去避免,实际上 `const` 变量的内部状态是可修改的。 幸运的是,`TypeScript` 允许你将对象的成员设置成只读的。接口一章有详细说明。## let vs. const现在我们有两种作用域相似的声明方式,我们自然会问到底应该使用哪个。与大多数泛泛的问题一样,答案是:依情况而定。使用最小特权原则,所有变量除了你计划去修改的都应该使用 `const`。 基本原则就是如果一个变量不需要对它写入,那么其它使用这些代码的人也不能够写入它们,并且要思考为什么会需要对这些变量重新赋值。使用 `const` 也可以让我们更容易的推测数据的流动。

4.解构

## 解构### 解构数组最简单的解构莫过于数组的解构赋值了:```typescript
let input = [1, 2]
let [first, second] = input
console.log(first) // outputs 1
console.log(second) // outputs 2
```这创建了 2 个命名变量 `first` 和 `second`。 相当于使用了索引,但更为方便:```typescript
let first = input[0]
let second = input[1]
```作用于函数参数:```typescript
let input: [number, number] = [1, 2]function f([first, second]: [number, number]) {console.log(first)console.log(second)
}f(input)
```你可以在数组里使用 `...` 语法创建剩余变量:```typescript
let [first, ...rest] = [1, 2, 3, 4]
console.log(first) // outputs 1
console.log(rest) // outputs [ 2, 3, 4 ]
```你也可以忽略你不关心的尾随元素:```typescript
let [first] = [1, 2, 3, 4]
console.log(first) // outputs 1
```或其它元素:```typescript
let [, second, , fourth] = [1, 2, 3, 4]
```### 对象解构你也可以解构对象:```typescript
let o = {a: 'foo',b: 12,c: 'bar'
}
let { a, b } = o```这通过 `o.a` 和 `o.b` 创建了 `a` 和 `b` 。 注意,如果你不需要 `c` 你可以忽略它。你可以在对象里使用 `...` 语法创建剩余变量:```typescript
let { a, ...passthrough } = o
let total = passthrough.b + passthrough.c.length
```### 属性重命名你也可以给属性以不同的名字:```typescript
let { a: newName1, b: newName2 } = o
```这里的语法开始变得混乱。 你可以将 `a: newName1` 读做 `"a 作为 newName1"`。 方向是从左到右,好像你写成了以下样子:```typescript
let newName1 = o.a
let newName2 = o.b
```令人困惑的是,这里的冒号不是指示类型的。 如果你想指定它的类型,仍然需要在其后写上完整的模式。```typescript
let {a, b}: {a: string, b: number} = o
```### 默认值默认值可以让你在属性为 `undefined` 时使用缺省值:```typescript
function keepWholeObject(wholeObject: { a: string, b?: number }) {let { a, b = 1001 } = wholeObject
}
```现在,即使 `b` 为 `undefined` , `keepWholeObject` 函数的变量 `wholeObject` 的属性 `a` 和 `b` 都会有值。### 函数声明解构也能用于函数声明。 看以下简单的情况:```typescript
type C = { a: string, b?: number }
function f({ a, b }: C): void {// ...
}
```
但是,通常情况下更多的是指定默认值,解构默认值有些棘手。 首先,你需要在默认值之前设置其格式。```typescript
function f({ a = '', b = 0 } = {}): void {// ...
}
f()
```> 上面的代码是一个类型推断的例子,将在后续章节介绍。其次,你需要知道在解构属性上给予一个默认或可选的属性用来替换主初始化列表。 要知道 C 的定义有一个 b 可选属性:```typescript
function f({ a, b = 0 } = { a: '' }): void {// ...
}
f({ a: 'yes' }) // OK, 默认 b = 0
f() // OK, 默认 a: '', b = 0
f({}) // Error, 一旦传入参数则 a 是必须的
```要小心使用解构。 从前面的例子可以看出,就算是最简单的解构表达式也是难以理解的。 尤其当存在深层嵌套解构的时候,就算这时没有堆叠在一起的重命名,默认值和类型注解,也是令人难以理解的。 解构表达式要尽量保持小而简单。

5.展开

## 展开```typescript
let first = [1, 2]
let second = [3, 4]
let bothPlus = [0, ...first, ...second, 5]
```
这会令 `bothPlus` 的值为 `[0, 1, 2, 3, 4, 5]`。 展开操作创建了 `first` 和 `second的` 一份浅拷贝。 它们不会被展开操作所改变。你还可以展开对象:```typescript
let defaults = { food: 'spicy', price: '$10', ambiance: 'noisy' }
let search = { ...defaults, food: 'rich' }
```search的值为 `{ food: 'rich', price: '$10', ambiance: 'noisy' }`。 对象的展开比数组的展开要复杂的多。像数组展开一样,它是从左至右进行处理,但结果仍为对象。这就意味着出现在展开对象后面的属性会覆盖前面的属性。因此,如果我们修改上面的例子,在结尾处进行展开的话:```typescript
let defaults = { food: 'spicy', price: '$10', ambiance: 'noisy' }
let search = { food: 'rich', ...defaults }
```那么,`defaults` 里的 `food` 属性会重写 `food: 'rich'`,在这里这并不是我们想要的结果。

ts从入门到进阶—3-4var/let/const声明,解构,展开相关推荐

  1. Vue3+TypeScript从入门到进阶(六)——TypeScript知识点——附沿途学习案例及项目实战代码

    文章目录 一.简介 二.Vue2和Vue3区别 三.Vue知识点学习 四.TypeScript知识点 一.JavaScript和TypeScript 二.TypeScript的安装和使用 1.Type ...

  2. Webpack从入门到进阶(二)---附沿路学习案例代码

    文章目录 Webpack从入门到进阶(一)---附沿路学习案例代码 一.Webpack简介 1.前端发展的几个阶段 2.前端三个框架的脚手架 3.Webpack是什么? 4.webpack和vite ...

  3. ab753变频器参数怎么拷贝到面板_【干货】一文让你从入门小白进阶为变频器高手...

    点击蓝字 关注我们 为确保 SINAMICS G120 的操作及监控便捷高效,提供了三种不同的操作面板: 1.基本操作面板(BOP-2). 2.智能操作面板(IOP-2) 3.智能连接模块(G120 ...

  4. 程序员编程如何入门、进阶?

    作者 | 码农唐磊 来源 | 程序猿石头(ID:tangleithu) 背景 在之前的这篇文章中,我谈了谈读本科的时候都学了哪些计算机专业课和推荐了一些经典的技术书籍,然后推文封面中的这张图引起了不少 ...

  5. 服务端工程师入门与进阶 Java 版

    前言 欢迎加入我们.这是一份针对实习生/毕业生的服务端开发入门与进阶指南.遇到问题及时问你的 mentor 或者直接问我. 建议: 尽量用google查找技术资料. 有问题在stackoverflow ...

  6. android自定义美颜相机完整程序,Android OpenGL ES从入门到进阶(一)—— 五分钟开发一款美颜相机...

    源码链接:https://github.com/smzhldr/AGLFramework 一.前言 商店里有数十款的美颜相机类产品,其实现原理基本上都是以OpenGL ES为核心的特效处理,大神可以忽 ...

  7. python数据结构推荐书-「算法与数据结构」从入门到进阶吐血整理推荐书单

    推荐一下「算法与数据结构」从入门到进阶的书单. 一.入门系列 这些书籍通过图片.打比方等通俗易懂的方法来讲述,让你能达到懂一些基础算法,线性表,堆栈,队列,树,图,DP算法,背包问题等,不要求会实现, ...

  8. 网站推广必备手册:SEO教程:搜索引擎优化入门与进阶(第2版)

    网站推广必备手册:SEO教程:搜索引擎优化入门与进阶(第2版) [作 者]吴泽欣 [同作者作品] [作译者介绍]  [丛 书 名] 图灵程序设计丛书  [出 版 社] 人民邮电出版社     [书 号 ...

  9. 新兴短距离无线通信技术ZigBee入门到进阶

    2019独角兽企业重金招聘Python工程师标准>>> 新兴短距离无线通信技术ZigBee入门到进阶 ZigBee技术是一种近距离.低复杂度.低功耗.低速率.低成本的双向无线通讯技术 ...

最新文章

  1. gitlab邮箱验证 邮箱提醒设置
  2. C++基础 (1) 常见的易错问题
  3. Bottle 框架中的装饰器类和描述符应用
  4. 协作机器人 ai算法_如果我们希望人工智能为我们服务而不是不利于我们,我们需要协作设计...
  5. 【OpenCV】OpenCV实战从入门到精通之 -- 离散傅里叶变换相关函数详解
  6. Android笔记 检测网络状态
  7. World Wind Java开发之七——读取本地栅格文件(影像+高程)构建三维场景(转)...
  8. 【工程项目经验】github定位到历史版本(历史commit点)
  9. Determining IP information for eth0...failed
  10. CCF201803-5 二次求和(100分题解链接)
  11. asp.net 页面跳转的方法
  12. 将外国文献翻译成中文
  13. 关系型数据库之MySQL8——由内而外的深化全面学习
  14. python 子域名爆破工具
  15. 【图文教程】Shell基础知识
  16. Excel VBA中的If,Select循环语句
  17. 黎曼的几何基础,维度
  18. android中的动画全解析
  19. vb.net 教程 8-3 数据库操作3
  20. bs架构 mysql_什么是bs架构

热门文章

  1. echarts结合react开发基础知识学习
  2. 数字电子钟——期末数电大作业Multisim版
  3. linux系统C语言程序的错误,linux下C语言错误整理
  4. mysql的触发器实验报告_数据库实验7触发器实验报告
  5. 2019年物联网无线市场供给侧观察
  6. JavaWeb解决form表单刷新提示确认重新提交表单
  7. Windows操作系统优化
  8. Android:根据文件大小自动转化为KB, MB, GB
  9. 高中计算机教师具备能力,新时代信息技术教师应具备哪些能力和素养
  10. mac 播放器MPV 下载地址