ES6 in Depth: Destructuring
编辑者说明:今天文章的早期版本是由Firefox工具开发工程师 Nick Fitzgerald 完成的,其起初来自 Nick 的博客 (Destructuring Assignment in ES6)[http://fitzgeraldnick.com/weblog/50/]
什么是解构的赋值
解构的赋值允许你给数组或者对象的变量赋值时,其语法类似于数组或者对象。这种语法非常简洁,其表达的意思也比传统的属性赋值要易懂。
没有解构的赋值,你可能访问一个三元素的数组时会如下:
1 2 3 |
|
通过解构的赋值,与其等价的代码,而且更为精确和可读:
1
|
|
SpiderMonky作为Firefox的JS的解释器,已经支持大部分的解构,但是并不是所有。(Track SpiderMonkey’s destructuring (and general ES6) support in bug 694100)[https://bugzilla.mozilla.org/show_bug.cgi?id=694100]。
数组和迭代的解构
我们已经在上面看到解构的赋值,其通用的语法如下:
1
|
|
这会将 array 中的元素对应地分配到 variable1 到 variableN。如果你想同时声明变量,你可以在分配的前面增加var
、let
、const
:
1 2 3 |
|
实际上,用variable
并不合适,因为你可以用你想的多深的内嵌模式(来分配值),如:
1 2 3 4 5 6 7 |
|
再者,你可以用解构来跳过数组的部分元素:
1 2 3 |
|
你还可以通过 “剩余”的模式来抓取数组中的尾部的元素:
1 2 3 |
|
当你访问数组中超边界或者不存在的元素时,你会得到通过索引返回的值:undefined.
1 2 3 4 5 6 |
|
注意,利用数组的分配模式的解构分配方式也适用于迭代中:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
解构对象
对对象解构可以让你将对象的不同属性绑定到变量中。当你指定要绑定的属性时,相关你绑定的变量的值就等于其属性值。
1 2 3 4 5 6 7 8 9 10 |
|
还有个有益的简单语法,当属性和变量的名称一致时:
1 2 3 4 5 |
|
类似数组的解构,你可以解构更多的内嵌和组合:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
当你解构的属性并没有定义时,你会得到 undefined
:
1 2 3 |
|
一个潜在的问题你应该了解,当你解构一个对象并分配到变量中时,但是你并没有声明它们(就是没有 let, const, 或者 var ):
1 2 |
|
之所以会发生这事,因为JS语法告诉解释引擎将{
开头的表达式认为是一个块表达式(例如,{ console }
就是个合法的块表达式)。解决方案还可以是将整个表达式用括号括起来:
1 2 |
|
解构的值不是对象、数组或者迭代
当你对null
和undefined
的解构时,你会得到错误:
1 2 |
|
但是,你可以解构其它原始的类型,如 布尔、数值、字符串,然后得到undefined
:
1 2 3 |
|
这结果有点意想不到,但是通过更多的测试可以其实其原因很简单。当我们使用对象的分配模式,其值要求可被强制解释为对象。多数类型可以转化为对象,但是null 和 undefined 不行。当使用数组分配模式时,值必须允许迭代。
默认值
当你解构的属性没有定义时,你可以提供默认值:
1 2 3 4 5 6 7 8 9 |
|
(笔者注:前第一二个特性已经在Firefox中实现了,但第三个没有,详见 bug 932080 )
解构的应用实践
定义函数参数
作为开发人员,我们希望使用更加合理的API,使用一个多属性的对象作为参数,而不是强制我们的用户来记住多个独立的参数(来作为函数的参数)。当我们想要引用其属性值时,我们可以使用解构来避免重复传入对象的属性。
1 2 3 |
|
这代码块来源于真实的代码,它来自Firefox的JS调试工具(也是JS的实现方式)。我们发现这模式令人很是愉快。
注册对象参数
扩展之前的例子,我们可以在解构时给予对象属性默认值。当我们存在的一个对象,这个对象是用来存储注册信息的,其部分属性已有合理的默认值时,解构就显得十分有好处了。例如,jQuery 的 ajax
函数传递一个注册对象作为第二个参数,可以重写成这样:
1 2 3 4 5 6 7 8 9 10 11 |
|
这避免对注册对象的每个属性重复地进行类如 var foo = config.foo || theDefaultFoo;
的情况。
(笔者注:不幸,在Firefox中,对象默认值的缩略语法并没有实现。我知道,我们已经有些段落已经进行了说明。详见 bug 932080 )
在ES6迭代协议中
ES6 定义了迭代的协议,我们已经在这系列文章的早期文章中有谈到。当你对 Map
进行迭代时,你会得到一系列的 [key, value]
键值对。我们可能解构这一对结构,可很容易访问基 key 和 value:
1 2 3 4 5 6 7 8 9 |
|
迭代时仅要 key :
1 2 3 |
|
或者迭代只要 value,
1 2 3 |
|
多个返回值
尽管JS语言并没有完全地支持返回多个值,但是这并不必要,因为你可以返回一个数组并对结果进行解构:
1 2 3 4 |
|
另外,你可以使用一个对象来作为容器,并命名返回的值:
1 2 3 4 5 6 7 |
|
其实,这两种模式更好的方式是将结果保存到临时容器中:
1 2 3 4 5 6 7 8 9 |
|
或者,可能使用延续传递的样式:
1 2 3 4 |
|
导入CommonJS的模块名称
还没有使用 ES6 的模块?还依然使用 CommonJS模块?没问题!当导入某些 CommonJS 模块 X 时,而模块 X 导出比你想要的还要更多的函数。利用解构,你可以精确地使用你想要使用的模块,避免命名的混乱:
1
|
|
总结
所以,你可以看到解构在很多细节上的应用都很有用,在 Mozilla 中我们已经对其有很多经验。Lars Hansen 在十年之前介绍过 Opera 中的解构,接着 Brendan Eich 为 Firefox 也增加了支持。我们知道,解构如果能应用于此语言的每天工作中,会使得所有的你的代码看起来更加的简洁。
五周之前,我们说过,ES6会改变你编写JS的方式。我们在大脑中有一系列的具体特性,每段时间学习到的东西都能使自己有些提升。合并在一起,它们将会最终影响到你工作中的每个项目。方式的改革引导变革。
顺应地应用ES6的解构需要团队的努力。特别感谢 Tooru Fujisawa (arai) 和 Arpad Borsos (Swatinem) 作出的贡献。
对于解构的支持,Chrome的开发工作正在进行中,其它的浏览器也毫无疑问地在以后的某时会支持。现在,如果你想在Web中使用解构,那么你需要使用 Babel 和 Traceur。
再次感谢 Nick Fitzgerald 的文章。
下周,我们将谈到一个特性,它或多或少地使用更简单的方式来改变你已经编写的JS代码,这些代码通常自始自终是语言的底层代码。你会关心吗?更为简洁的语法你是否会兴奋呢?我确信答案是肯定的,但是先不要回答我。在下周加入我们并进行了解,我们将会深入 箭头函数(arrow function)。