参考变量声明 · TypeScript中文网 · TypeScript——JavaScript的超集 (tslang.cn)
变量声明
let与var有很多相似之处,但let能够更好地帮助开发者避免一些问题。const是let的增强,它会阻止对被const修饰过的变量的再次赋值。
var声明可以在包含它的函数、模块、命名空间或全局作用域的任何地方被访问,包含它的代码块难以限制var声明的变量的作用域。这使得多次声明同一变量时不会报错,但是在实际开发中容易引发许多问题。
let与var的区别不在语法上,而是关乎语义。
当用let声明一个变量时,它使用的是词法作用域或块作用域。块作用域声明的变量无法在声明它的函数或者循环外被访问到,catch语句也是如此。块级作用域声明的变量的另一个特点是不能在声明之前对其进行读或写。
不过,我们仍然可以在变量声明前获取它,只是不能在变量声明前去调用获取该变量的函数。
function foo() {
// okay to capture 'a'
return a;
}
// 不能在'a'被声明前调用'foo'
// 运行时应该抛出错误
foo();
let a;
重定义和屏蔽
使用var时,无论声明多少次,最后都只用得到同一个变量。
而let声明将不会允许对变量进行重复的声明。当然,块级作用域变量不是不能使用函数作用域内的变量进行声明,而是块级作用域变量需要在明显不同的块里声明。
function f(condition, x) {
if (condition) {
let x = 100;
return x;
}
return x;
}
f(false, 0); // returns 0
f(true, 0); // returns 100
在一个嵌套作用域内声明一个新变量的行为叫做屏蔽,可能诱发一些新问题也可能能够解决一些问题。
function sumMatrix(matrix: number[][]) {
let sum = 0;
for (let i = 0; i < matrix.length; i++) {
var currentRow = matrix[i];
for (let i = 0; i < currentRow.length; i++) {
sum += currentRow[i];
}
}
return sum;
}
块级作用域变量的获取
当使用var声明时,每次进入到一个新的作用域,它都会创建一个新的变量环境。
const声明变量
使用const声明的变量与let有着相同的作用域规则,但是不能够对它进行重新赋值。
某些时候,你也能够通过特殊的方式来避开这一规则来修改某个const变量的内部状态。
const numLivesForCat = 9;
const kitty = {
name: "Aurora",
numLives: numLivesForCat,
}
// Error
kitty = {
name: "Danielle",
numLives: numLivesForCat
};
// 正确
kitty.name = "Rory";
kitty.name = "Kitty";
kitty.name = "Cat";
kitty.numLives--;
解构数组
相当于引用了数组的索引,但是更加简单方便。
let input = [1, 2];
let [first, second] = input;
console.log(first); // 输出 1
console.log(second); // 输出 2
let [thefirst] = input;
console.log(thefirst); // 输出 1
可以作用于函数参数
function f([first, second]: [number, number]) {
console.log(first);
console.log(second);
}
f(input);
在数组中可以用...语法来创建剩余变量
let [first,...rest] = [1,2,3,4];
console.log(first); // 输出1
console.log(rest); // 输出[2,3,4]
解构对象
通过obj.a和obj.b可以将对象的属性解构出来并且赋给{}内的变量,可以忽略不需要解构出来的属性。
let obj = {
a: "it is a",
b:3,
c:"Haha"
};
let { a, b} = obj;
console.log(a); // 输出 it is a
console.log(b); // 输出 3
解构没有声明的对象
({ a, b } = { a: "baz", b: 101 });
也可以用...语法创建剩余变量。
属性重命名
let { a: newName1, b: newName2 } = obj;
函数声明运用解构
在函数声明中运用解构通常会使得代码变得难以理解,在函数中解构通常用于函数声明和指定默认值。
用于函数声明的情况。
type C = {a:string, b:number};
// 函数参数是C, C中有一个string类型的属性a, 一个number类型的属性b
function f({a, b}: C):void {
// ...
}
用于指定默认值的情况。
// 先为参数a赋默认值" ", 再为参数b赋默认值b = 0
function f({a, b=0} = {a: " "}): void {
// ...
}
默认值
当属性值为undefined时,该属性可以拥有默认值。
function keepWholeObject(wholeObject: { a: string, b?: number }) {
let { a, b = 1001 } = wholeObject;
}
展开
展开与解构恰好相反。展开支持将一个数组展开为另一个数组,将一个对象展开为另一个对象。
展开操作将为被展开对象创建一个浅拷贝(引用仍然一致),被展开的对象并不会被展开操作所改变。
对象的展开要稍麻烦一些,展开对象时从左到右进行处理,后面的属性会覆盖前面的属性,但结果仍为对象。
// 数组的展开
let first = [1, 2];
let second = [3, 4];
let array = [0, ...first, ...second, 10]; // array 为 [0, 1, 2, 3, 4, 10]
// 对象的展开
let default = {food: "spicy", price: "$15", ambiance: "noisy"};
let search = { ...default, food: "rich" }; // search 为 {food: "rich", price: "$15", ambiance: "noisy"};