CONTENT

Another TypeScript已经可以解析其它 ECMAScript 2015 特性了。 完整列表请参见:

the article on the Mozilla Developer Network

按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。 本章,我们将简单研究一下。

解构数组

最简单的解构莫过于数组的解构赋值。如:


let input =[1,2];
let [first,second] = input;
console.log(first);//1
console.log(second);//2

在这里,创建了两个命名变量 firstsecond 。相当于使用了索引,但更加方便。如:


first = input[0];
second = input[1];

解构作用于已声明的变量会更好。如:


// swap variables
[first, second] = [second, first];

作用域函数参数。如:


let input : [number,number] =[1,2];//注意,此处必须给input加上类型,否则在后面传参会提示错误
function fun([first, second] : [number, number]) : void
{
    console.log(first);
    console.log(second);
}
fun(input);

在数组中使用 ... 语法创建剩余变量。如:


let [first,...rest] = [1,2,3,4];
console.log(first);//1  number类型
console.log(rest);//2,3 数组类型[number]

忽略不关心的尾随元素。如:


let [first] = [1, 2, 3, 4];
console.log(first); // outputs 1

或其他元素。如:


let [, second, , fourth] = [1, 2, 3, 4];

对象解构

除了能够解构数组外,我们还可以解构对象。如:


let o = {
    a: "foo",
    b: 12,
    c: "bar"
};
let { a, b } = o;
console.log(a);//foo

此处通过o.ao.b 创建了 ab 。如果你不需要用到 c ,你可以忽略它。

类似数组解构,用没有声明的赋值。如:


({ a, b } = { a: "baz", b: 101 });
注:此处需要用小括号括起来。

当然,类似数组,我们同样可以使用 ... 创建剩余变量。如:


let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;

属性重命名

你也可以给属性命名不同的名字


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

注意,这里的a: newName1应该读作 a 作为 newName1,方向是从左到右,相当于:


let newName1 = o.a;
let newName2 = o.b;

此处的冒号并非指示类型,如果你要指示类型的话需要在其后面使用完整模式,如下:


let {a, b}: {a: string, b: number} = o;

默认值

默认值可以让你在属性为 undefined 时使用缺省值:


function keepName(name: { chineseName: string, englishName?: string }) {
    let { chineseName, englishName = "Jane" } = name;
}

函数声明

解构也能用于函数声明。 看以下简单的情况:


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

但是,通常情况下更多的是指定默认值,解构默认值有些棘手。 首先,你需要在默认值之前设置其格式。


function f({ a="", b=0 } = {}): void {
    // ...
}
f();

其次,你需要知道在解构属性上给予一个默认或可选的属性用来替换主初始化列表。 要知道 C 的定义有一个 b 可选属性:


function f({ a, b = 0 } = { a: "" }): void {
    // ...
}
f({ a: "yes" }); // ok, default b = 0
f(); // ok, default to {a: ""}, which then defaults b = 0
f({}); // error, 'a' is required if you supply an argument

要小心使用解构。 从前面的例子可以看出,就算是最简单的解构表达式也是难以理解的。 尤其当存在深层嵌套解构的时候,就算这时没有堆叠在一起的重命名,默认值和类型注解,也是令人难以理解的。 解构表达式要尽量保持小而简单。 你自己也可以直接使用解构将会生成的赋值表达式。

展开

展开操作符正与解构相反。 它允许你将一个数组展开为另一个数组,或将一个对象展开为另一个对象。 例如:


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

这会令bothPlus的值为[0, 1, 2, 3, 4, 5]。 展开操作创建了 firstsecond的一份浅拷贝。 它们不会被展开操作所改变。

你还可以展开对象:


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

search的值为{ food: "rich", price: "$$", ambiance: "noisy" }。 对象的展开比数组的展开要复杂的多。 像数组展开一样,它是从左至右进行处理,但结果仍为对象。 这就意味着出现在展开对象后面的属性会覆盖前面的属性。 因此,如果我们修改上面的例子,在结尾处进行展开的话:


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

那么,defaults里的food属性会重写food: "rich"search的值为{ food: "noisy", price: "$$", ambiance: "noisy" },在这里这并不是我们想要的结果。

对象展开还有其它一些意想不到的限制。 首先,它仅包含对象 自身的可枚举属性。 大体上是说当你展开一个对象实例时,你会丢失其方法:


class C {
  p = 12;
  m() {
  }
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // error!

其次,TypeScript编译器不允许展开泛型函数上的类型参数。 这个特性会在TypeScript的未来版本中考虑实现。

你可以在下面的在线编辑器中亲自尝试一下:

Comments | NOTHING

暂无评论...