深入浅出ES6(六):解构 Destructuring
作者 Jason Orendorff github主页 https://github.com/jorendorff
什么是解构赋值?
解构赋值允许你使用类似数组或对象字面量的语法将数组和对象的属性赋给各种变量。这种赋值语法极度简洁,同时还比传统的属性访问方法更为清晰。
通常来说,你很可能这样访问数组中的前三个元素:
var first = someArray[0];
var second = someArray[1];
var third = someArray[2];
如果使用解构赋值的特性,将会使等效的代码变得更加简洁并且可读性更高:
var [first, second, third] = someArray;
SpiderMonkey(Firefox的JavaScript引擎)已经支持解构的大部分功能,但是仍不健全。你可以通过bug 694100跟踪解构和其它ES6特性在SpiderMonkey中的支持情况。
数组与迭代器的解构
以上是数组解构赋值的一个简单示例,其语法的一般形式为:
[ variable1, variable2, ..., variableN ] = array;
这将为variable1到variableN的变量赋予数组中相应元素项的值。如果你想在赋值的同时声明变量,可在赋值语句前加入var
、let
或const
关键字,例如:
var [ variable1, variable2, ..., variableN ] = array;
let [ variable1, variable2, ..., variableN ] = array;
const [ variable1, variable2, ..., variableN ] = array;
事实上,用变量
来描述并不恰当,因为你可以对任意深度的嵌套数组进行解构:
var [foo, [[bar], baz]] = [1, [[2], 3]];
console.log(foo);
// 1
console.log(bar);
// 2
console.log(baz);
// 3
此外,你可以在对应位留空来跳过被解构数组中的某些元素:
var [,,third] = ["foo", "bar", "baz"];
console.log(third);
// "baz"
而且你还可以通过“不定参数”模式捕获数组中的所有尾随元素:
var [head, ...tail] = [1, 2, 3, 4];
console.log(tail);
// [2, 3, 4]
当访问空数组或越界访问数组时,对其解构与对其索引的行为一致,最终得到的结果都是:undefined
。
console.log([][0]);
// undefined
var [missing] = [];
console.log(missing);
// undefined
请注意,数组解构赋值的模式同样适用于任意迭代器:
function* fibs() {
var a = 0;
var b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
var [first, second, third, fourth, fifth, sixth] = fibs();
console.log(sixth);
// 5
对象的解构
通过解构对象,你可以把它的每个属性与不同的变量绑定,首先指定被绑定的属性,然后紧跟一个要解构的变量。
var robotA = { name: "Bender" };
var robotB = { name: "Flexo" };
var { name: nameA } = robotA;
var { name: nameB } = robotB;
console.log(nameA);
// "Bender"
console.log(nameB);
// "Flexo"
当属性名与变量名一致时,可以通过一种实用的句法简写:
var { foo, bar } = { foo: "lorem", bar: "ipsum" };
console.log(foo);
// "lorem"
console.log(bar);
// "ipsum"
与数组解构一样,你可以随意嵌套并进一步组合对象解构:
var complicatedObj = {
arrayProp: [
"Zapp",
{ second: "Brannigan" }
]
};
var { arrayProp: [first, { second }] } = complicatedObj;
console.log(first);
// "Zapp"
console.log(second);
// "Brannigan"
当你解构一个未定义的属性时,得到的值为undefined
:
var { missing } = {};
console.log(missing);
// undefined
请注意,当你解构对象并赋值给变量时,如果你已经声明或不打算声明这些变量(亦即赋值语句前没有let
、const
或var
关键字),你应该注意这样一个潜在的语法错误:
{ blowUp } = { blowUp: 10 };
// Syntax error 语法错误
为什么会出错?这是因为JavaScript语法通知解析引擎将任何以{开始的语句解析为一个块语句(例如,{console}
是一个合法块语句)。解决方案是将整个表达式用一对小括号包裹:
({ safe } = {});
// No errors 没有语法错误
解构值不是对象、数组或迭代器
当你尝试解构null
或undefined
时,你会得到一个类型错误:
var {blowUp} = null;
// TypeError: null has no properties(null没有属性)
然而,你可以解构其它原始类型,例如:布尔值
、数值
、字符串
,但是你将得到undefined
:
var {wtf} = NaN;
console.log(wtf);
// undefined
你可能对此感到意外,但经过进一步审查你就会发现,原因其实非常简单。当使用对象赋值模式时,被解构的值需要被强制转换为对象。大多数类型都可以被转换为对象,但null
和undefined
却无法进行转换。当使用数组赋值模式时,被解构的值一定要包含一个迭代器。
默认值
当你要解构的属性未定义时你可以提供一个默认值:
var [missing = true] = [];
console.log(missing);
// true
var { message: msg = "Something went wrong" } = {};
console.log(msg);
// "Something went wrong"
var { x = 3 } = {};
console.log(x);
// 3
(译者按:Firefox目前只实现了这个特性的前两种情况,第三种尚未实现。详情查看bug 932080。)
解构的实际应用
函数参数定义
作 为开发者,我们需要实现设计良好的API,通常的做法是为函数为函数设计一个对象作为参数,然后将不同的实际参数作为对象属性,以避免让API使用者记住 多个参数的使用顺序。我们可以使用解构特性来避免这种问题,当我们想要引用它的其中一个属性时,大可不必反复使用这种单一参数对象。
function removeBreakpoint({ url, line, column }) {
// ...
}
这是一段来自Firefox开发工具JavaScript调试器(同样使用JavaScript实现——没错,就是这样!)的代码片段,它看起来非常简洁,我们会发现这种代码模式特别讨喜。
配置对象参数
延伸一下之前的示例,我们同样可以给需要解构的对象属性赋予默认值。当我们构造一个提供配置的对象,并且需要这个对象的属性携带默认值时,解构特性就派上用场了。举个例子,jQuery的ajax
函数使用一个配置对象作为它的第二参数,我们可以这样重写函数定义:
jQuery.ajax = function (url, {
async = true,
beforeSend = noop,
cache = true,
complete = noop,
crossDomain = false,
global = true,
// ... 更多配置
}) {
// ... do stuff
};
如此一来,我们可以避免对配置对象的每个属性都重复var foo = config.foo || theDefaultFoo;
这样的操作。
(编者按:不幸的是,对象的默认值简写语法仍未在Firefox中实现,我知道,上一个编者按后的几个段落讲解的就是这个特性。点击bug 932080查看最新详情。)
与ES6迭代器协议协同使用
ECMAScript 6中定义了一个迭代器协议,我们在《深入浅出ES6(二):迭代器和for-of循环》中已经详细解析过。当你迭代Maps(ES6标准库中新加入的一种对象)后,你可以得到一系列形如[key, value]
的键值对,我们可将这些键值对解构,更轻松地访问键和值:
var map = new Map();
map.set(window, "the global");
map.set(document, "the document");
for (var [key, value] of map) {
console.log(key + " is " + value);
}
// "[object Window] is the global"
// "[object HTMLDocument] is the document"
只遍历键:
for (var [key] of map) {
// ...
}
或只遍历值:
for (var [,value] of map) {
// ...
}
多重返回值
JavaScript语言中尚未整合多重返回值的特性,但是无须多此一举,因为你自己就可以返回一个数组并将结果解构:
function returnMultipleValues() {
return [1, 2];
}
var [foo, bar] = returnMultipleValues();
或者,你可以用一个对象作为容器并为返回值命名:
function returnMultipleValues() {
return {
foo: 1,
bar: 2
};
}
var { foo, bar } = returnMultipleValues();
这两个模式都比额外保存一个临时变量要好得多。
function returnMultipleValues() {
return {
foo: 1,
bar: 2
};
}
var temp = returnMultipleValues();
var foo = temp.foo;
var bar = temp.bar;
或者使用CPS变换:
function returnMultipleValues(k) {
k(1, 2);
}
returnMultipleValues((foo, bar) => ...);
使用解构导入部分CommonJS模块
你是否尚未使用ES6模块?还用着CommonJS的模块呢吧!没问题,当我们导入CommonJS模块X时,很可能在模块X中导出了许多你根本没打算用的函数。通过解构,你可以显式定义模块的一部分来拆分使用,同时还不会污染你的命名空间:
const { SourceMapConsumer, SourceNode } = require("source-map");
(如果你使用ES6模块,你一定知道在import
声明中有一个相似的语法。)
文后盘点
所以,正如你所见,解构在许多独立小场景中非常实用。在Mozilla我们已经积累了许多有关解构的使用经验。十年前,Lars Hansen在Opera中引入了JS解构特性,Brendan Eich随后就给Firefox也增加了相应的支持,移植时版本为Firefox 2。所以我们可以肯定,渐渐地,你会在每天使用的语言中加入解构这个新特性,它可以让你的代码变得更加精简整洁。
在第一篇文章中,我说过ES6很可能改变你写JavaScript的方式。这正是我日思夜想的特性:轻松学习,简单改进,合力出击,优化项目,在不断的进化中改革这门语言。
感谢团队对于整个ES6解构特性的努力,特别感谢Tooru Fujisawa(arai)和Arpad Borsos(Swatinem)的出色贡献。
Chrome中有关解构的支持正在开发中,其它浏览器也将适时增加支持。现在,如果你想在Web上使用解构功能,你需要使用Babel或Traceur将ES6代码转译为相应的ES5代码。
再次感谢Nick Fitzgerald撰写ES6的解构特性。
深入浅出ES6(六):解构 Destructuring的更多相关文章
- ES6 对象解构
ES6 对象解构 第一眼看到,什么鬼? const { body } = document `` 其实等于: const body = document.body ``` http://es6.rua ...
- 深入理解ES6之解构
变量赋值的痛 对象 let o = {a:23,b:34}; let a = o.a; let b = o.b; 如上文代码,我们经常会遇到在各种场合需要获取对象中的值的场景,舒服一点的是获取单个属性 ...
- ES6 的解构赋值前每次都创建一个对象吗?会加重 GC 的负担吗?
本文来源于知乎上的一个提问. 为了程序的易读性,我们会使用 ES6 的解构赋值: function f({a,b}){} f({a:1,b:2}); 这个例子的函数调用中,会真的产生一个对象吗?如果会 ...
- ES6 解构 destructuring
解构的作用:简化书写长度,提升开发效率. 解构对象 在开发中我们常用到使用ajax请求数据,并且把数据渲染到页面上.可能这个数据返回的对象或数组.例如返回一个obj{name:'zwq',age:18 ...
- Es6 新增解构赋值
1.数组的解构赋值 基本用法 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). 要想实现解构,就必须是容器,或者具有可遍历的接口. 以前,为 ...
- ES6变量解构赋值的用法
一.数组赋值(从数组中提取值,按照对应位置,对变量赋值) 1. 完全解构(变量与值数目相等) let arr = [1, 2, 3]; let [a,b,c] = arr; console.log(a ...
- es6的解构函数
话说,解构无处不在啊,鄙人自从用了vue写项目以来,总是遇到各路大神莫名其妙的写法,然并未出任何错,查之,然解构也,呜呼哀哉,进而习之. 解构(Destructuring):是将一个数据结构分解为更小 ...
- ES6之解构赋值
截止到ES6,共有6种声明变量的方法,分别是var .function以及新增的let.const.import和class: 我们通常的赋值方法是: var foo='foo'; function ...
- 进军es6(2)---解构赋值
本该两周之前就该总结的,但最近一直在忙校招实习的事,耽误了很久.目前依然在等待阿里HR面后的结果中...但愿好事多磨!在阿里的某轮面试中面试官问到了es6的掌握情况,说明es6真的是大势所趋,我们更需 ...
随机推荐
- AndroidStudio中gradle异常:unexpected end of block data
原因:可能是Android buildTools版本不够高. 解决方法:打开build.gradle,将android中buildToolsVersion改为'20.0.0' (我使用的是gradle ...
- !!! FAILED BINDER TRANSACTION !!! TransactionTooLargeException
- ::): !!! FAILED BINDER TRANSACTION !!! xxxRecorder 运行40多分钟,崩溃,捕获日志 03-12 14:50:12.353: E/JavaBinde ...
- ARP协议详解
ARP协议:地址解析协议,将IP地址映射到MAC地址. ARP缓存:每个主机都有存储IP地址和MAC地址的缓冲区.每条记录最长生存时间为10分钟,如果一条记录2分钟没有使用,则会被删除.如果始终在使用 ...
- Linux C 程序 Linux网络编程(21)
Linux网络编程网络编程必备的理论基础网络模型,地址,端口,TCP/IP协议 TCP/IP协议是目前世界上使用最广泛的网络通信协议日常中的大部分应用使用该系列协议(浏览网页,收发电子邮件,QQ聊天等 ...
- 转:12种JavaScript MVC框架之比较
Gordon L. Hempton是西雅图的一位黑客和设计师,他花费了几个月的时间研究和比较了12种流行的JavaScript MVC框架,并在博客中总结了每种框架的优缺点,最终的结果是,Ember. ...
- 用泛型的IEqualityComparer<T>接口去重复项
提供者:porschev 题目:下列数据放在一个List中,当ID和Name都相同时,去掉重复数据 ID Name 1 张三 1 李三 1 小伟 1 李三 2 李四 2 李武 ----- ...
- Oracle中排序列中值相同引发的问题(译)
This queston came up on the Oracle newsgroup a few days ago: 这个问题在Oracle的新闻中心被提出了一段时间: I have a tabl ...
- Hadoop上路-01_Hadoop2.3.0的分布式集群搭建
一.配置虚拟机软件 下载地址:https://www.virtualbox.org/wiki/downloads 1.虚拟机软件设定 1)进入全集设定 2)常规设定 2.Linux安装配置 1)名称类 ...
- hi,mongo!(1)
用了很多年的关系型数据库,想换一种思路,学习一下最近比较火的mongo数据库. 一.下载.安装mongo 下载地址:http://www.mongodb.org/downloads(官网) 官网的mo ...
- NodeJs菜鸟初始
我们先来了解下什么是nodejs 一.nodejs具有事件驱动.异步编程的特点. 事件驱动这个词并不陌生,在某些传统语言的网络编程中,我们会用到回调函数,比如当socket资源达到某种状态时,注册的回 ...