不像C#这种强类型语言,JavaScript是一种弱类型语言,一个变量可以被赋值成各种类型的值。
我们在C#里声明一个string类型变量: string cSharpString = "a c# string";
在JavaScript里面直接用var,不指定类型,它自己通过值来推导类型: var javaScriptString = "a JavaScript string";
看了很多描述JavaScript var关键字的文章,大多都在说它有很多坑,因为它的作用于非常奇怪,所以有很多怪异行为,又要通过很多怪异的解决方案来避免这些怪异行为。
很庆幸现在可以直接使用let和const,不用被var折磨,致敬JavaScript大神们!
TypeScript是JavaScript的超集,所以它本身就支持let和const.
let :变量,值可变。
const :常量,对let的增强,阻止被再次赋值。
let声明
let hello = "Hello!";
使用 let 声明一个变量,它使用得是块作用域。看了一下,发现就跟C#变量作用域一样。
- 块作用域变量在包含他们的块或 for 循环之外不能访问。
- 变量不能在声明前使用,就是使用变量的代码必须写在变量声明的后面。变量存在于它的作用域内里,作用域内、变量没声明之前的代码区域属于变量的暂时性死区,不能用它。
- 同一作用域内,变量只能声明一次,与C#一样。
- 嵌套作用域,内层作用域变量会屏蔽外层作用域同名变量,与C#一样。
const声明
const numLivesForCat = 9;
与 let 的唯一区别就是,被赋值后不能再改变。
所谓的不能改变,只是说引用的值不可变。
例如,const常量被赋值成一个对象,不可以给这个常量赋成新的对象,却可以改变原来对象的成员。
1 const numLivesForCat = 9; 2 const kitty = { 3 name: "Aurora", 4 numLives: numLivesForCat, 5 } 6 7 // Error 8 kitty = { 9 name: "Danielle", 10 numLives: numLivesForCat 11 }; 12 13 // all "okay" 14 kitty.name = "Rory"; 15 kitty.name = "Kitty"; 16 kitty.name = "Cat"; 17 kitty.numLives--;
解构赋值
将部分值从数组中取出,或者部分属性从对象中取出,赋值给其他变量。这里简短概述。
解构数组
使用场景 | 应用举例 |
变量赋值 |
我们将数组的两个值取出来分别赋给两个变量。 1 let input = [1, 2];
2
3 // 旧写法,使用索引按个赋值
4 let first = input[0];
5 let second = input[1];
6
7 // 使用数组解构赋值
8 let [first, second] = input;
9
10 // 两种方式输出结果一样
11 console.log(first); // outputs 1
12 console.log(second); // outputs 2
|
变量值置换 |
// swap variables [first, second] = [second, first]; |
函数参数 |
function f([first, second]: [number, number]) { console.log(first); console.log(second); } f(input); |
使用展开运算符 ... 创建剩余变量 |
|
忽略不关心的尾随元素 |
|
忽略不关心的任意元素 |
|
对象解构
使用场景 | 应用举例 |
变量赋值 |
我们将对象的属性取出来分别赋值给两个变量 let o = { a: "foo", b: 12, c: "bar" }; // 旧写法 let a = o.a; let b = o.b; // 用对象解构赋值,不用c就忽略它 let { a, b } = o; |
使用展开运算符 ...创建剩余变量 |
let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;
|
解构赋值给与属性不同名变量 |
let { a: newName1, b: newName2 } = o; // 上面的写法等同于下面的旧写法 let newName1 = o.a; let newName2 = o.b; 这里的语法开始变得混乱,冒号不是用来表示类型,而是指对象属性赋值给冒号后面的变量。 要想指定类型,需要在后面写上完整的模式: let { a: newName1, b: newName2 }: { a: string, b:number} = o;
|
解构用于函数声明 |
function f({ a, b }: { a: string, b?: number } = { a: "", b: 0}): void { // ... } |
解构赋值同时指定默认值 |
默认值会在对应属性为undfined时被使用。 function keepWholeObject(wholeObject: { a: string, b?: number }) { let { a, b = 1001 } = wholeObject; } wholeObject.b为undefined时,b会被赋值成1001. |
展开运算符
在函数调用、构造字面量数组时,将数组表达式或string在语法层面展开;
在构造字面量对象时,将对象表达式按key-value方式展开。
构造字面量数组,是指 let arr = ["hello", "world", "hahaha"]; 这种简洁的构造方式。
构造字面量对象,是指 let person = {name: "bala", age: 18}; 这种简洁的构造方式。
用展开运算符构造字面量数组:
let first = [1, 2]; let second = [3, 4]; let bothPlus = [0, ...first, ...second, 5]; // [0, 1, 2, 3, 4, 5]
展开操作创建了first和second的浅拷贝,它们本身不会被展开操作改变。
用展开运算符构造字面量对象:
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" }; let search = { ...defaults, food: "rich" }; // { food: "rich", price: "$$", ambiance: "noisy" }
出现在后面的属性会覆盖前面的相同属性,这里后面的 food: "rich" 覆盖了前面的 food: "spicy" 。
另外,对象展开结果只会包含自己的可枚举属性,大体上是说展开对象实例会丢失其方法。