Javascript ES6的解构
数组是Javascript有关集合列表的表达方式,列表集合如此重要,是因为它是代表事物有顺序的集合,一个顺序集合是对现实世界的基本抽象方式。
数组的语法如下:
[1]
//=> [1]
[2, 3, 4]
//=> [2,3,4]
表达式也可以在数组中使用:
[ 2,
3,
2 + 2
]
//=> [2,3,4]
下面看看使用Javascript ES6的数组字面量Array literal(又翻译为字面量、字面常量)的表达式:
const wrap = (something) => [something];
wrap("lunch")
//=> ["lunch"]
数组字面量Array literal是一个表达式,而数组是一个引用类型,每次数组字面量被调用时都会计算赋值,我们总是得到一个新的不同的数组,尽管这些数组包含同样的数组元素。
[] === []
//=> false
[2 + 2] === [2 + 2]
//=> false
const array_of_one = () => [1];
array_of_one() === array_of_one()
//=> false
下面看看如何分解数组,也就是通过函数进行数组结构分解,Destructuring解构是Common Lisp中的一个特性,我们可以使用“[,表达式,]”构造construct一个数组字面量,下面是一个使用名称的数组字面量:
const wrap = (something) => [something];
我们使用代码块和外部名称扩展一下它:
const wrap = (something) => {
const wrapped = [something];
return wrapped;
}
wrap("package")
//=> ["package"]
上面代码中const wrapped = [something]; 这一句(第二行)非常有意思,左边是要绑定的名称,右边是数组字面量,也就是一种构造数组的模板,这非常像quasi-literal字符串。
在JS中我们可以反转语句,将模板放在左边,值放在右边:
const unwrap = (wrapped) => {
const [something] = wrapped;
return something;
}
unwrap(["present"])
//=> "present"
注意第二行语句const [something] = wrapped;解构了wrapped代表的数组,将分解后的值绑定到一个只有单个数组元素的something,这里只是分解出一个元素,我们还可以分解出多个元素:
const surname = (name) => {
const [first, last] = name;
return last;
}
surname(["Zhang", "San"])
//=> "San"
上述代码分解出姓first和名last,最后该函数返回的是的名last的值。虽然这种效果可以使用:
(name) => name[1]
这种普通方式来分解获取,但是解构是一种重新组装使用数据的代码风格,一种valuable代码风格。
解构可以嵌套:
const description = (nameAndOccupation) => {
const [[first, last], occupation] = nameAndOccupation;
return `${first} is a ${occupation}`;
}
description([["Zhang", "San"], "programmer"])
//=> "Zhang is a programmer"
收集gathering
有时我们需要从一个数组中取出中另外一个数组,比如下面代码是从数组中排除头部,然后收集除了头部以外的元素:
const [car, ...cdr] = [1, 2, 3, 4, 5];
car
//=> 1
cdr
//=> [2, 3, 4, 5]
这里使用了“...”来表达收集,但是它不能提供通用的模式匹配能力,比如,我们不能这样写:
const [...butLast, last] = [1, 2, 3, 4, 5];
//=> ERROR
const [first, ..., last] = [1, 2, 3, 4, 5];
//=> ERROR
在构造器中“...”可以在前面,比如:
const date = new Date(...[2015, 1, 1]);
请注意当我们引入了解构destructuring时,这其实是一种数组字面量的顺序问题,如果:
const wrapped = [something];
那么:
const [unwrapped] = something;
现在问题来了,上面展示了解构的反转方式,那么什么是收集gathering的反转呢?
比如前面案例:
const [car, ...cdr] = [1, 2, 3, 4, 5];
它的反转是:
const cons = [car, ...cdr];
看看下面代码:
const oneTwoThree = ["one", "two", "three"];
["zero", ...oneTwoThree]
//=> ["zero","one","two","three"]
这段代码可以运行,将一个数组的元素放入另外一个数组,使用“...”解构是“收集gathering”,而在字面量literal中将元素插入称为“扩展spreading”
解构参数
考虑下面场景,我们传入参数:
foo()
bar("smaug")
baz(1, 2, 3)
这非常类似数组字面量,再看看将值绑定到参数名称:
const foo = () => ...
const bar = (name) => ...
const baz = (a, b, c) => ...
这非常类似解构,但是有一点区别,我们并不试图收集什么,比如:
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)
//=> [1,2,3,4,5]
const headAndTail = (head, ...tail) => [head, tail];
headAndTail(1, 2, 3, 4, 5)
//=> [1,[2,3,4,5]]