作者 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的变量赋予数组中相应元素项的值。如果你想在赋值的同时声明变量,可在赋值语句前加入varletconst关键字,例如:

    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

请注意,当你解构对象并赋值给变量时,如果你已经声明或不打算声明这些变量(亦即赋值语句前没有letconstvar关键字),你应该注意这样一个潜在的语法错误:

    { blowUp } = { blowUp: 10 };
// Syntax error 语法错误

为什么会出错?这是因为JavaScript语法通知解析引擎将任何以{开始的语句解析为一个块语句(例如,{console}是一个合法块语句)。解决方案是将整个表达式用一对小括号包裹:

    ({ safe } = {});
// No errors 没有语法错误

  

解构值不是对象、数组或迭代器

当你尝试解构nullundefined时,你会得到一个类型错误:

    var {blowUp} = null;
// TypeError: null has no properties(null没有属性)

然而,你可以解构其它原始类型,例如:布尔值数值字符串,但是你将得到undefined

    var {wtf} = NaN;
console.log(wtf);
// undefined

你可能对此感到意外,但经过进一步审查你就会发现,原因其实非常简单。当使用对象赋值模式时,被解构的值需要被强制转换为对象。大多数类型都可以被转换为对象,但nullundefined却无法进行转换。当使用数组赋值模式时,被解构的值一定要包含一个迭代器

默认值

当你要解构的属性未定义时你可以提供一个默认值:

  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上使用解构功能,你需要使用BabelTraceur将ES6代码转译为相应的ES5代码。


再次感谢Nick Fitzgerald撰写ES6的解构特性。

深入浅出ES6(六):解构 Destructuring的更多相关文章

  1. ES6 对象解构

    ES6 对象解构 第一眼看到,什么鬼? const { body } = document `` 其实等于: const body = document.body ``` http://es6.rua ...

  2. 深入理解ES6之解构

    变量赋值的痛 对象 let o = {a:23,b:34}; let a = o.a; let b = o.b; 如上文代码,我们经常会遇到在各种场合需要获取对象中的值的场景,舒服一点的是获取单个属性 ...

  3. ES6 的解构赋值前每次都创建一个对象吗?会加重 GC 的负担吗?

    本文来源于知乎上的一个提问. 为了程序的易读性,我们会使用 ES6 的解构赋值: function f({a,b}){} f({a:1,b:2}); 这个例子的函数调用中,会真的产生一个对象吗?如果会 ...

  4. ES6 解构 destructuring

    解构的作用:简化书写长度,提升开发效率. 解构对象 在开发中我们常用到使用ajax请求数据,并且把数据渲染到页面上.可能这个数据返回的对象或数组.例如返回一个obj{name:'zwq',age:18 ...

  5. Es6 新增解构赋值

    1.数组的解构赋值 基本用法 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). 要想实现解构,就必须是容器,或者具有可遍历的接口. 以前,为 ...

  6. ES6变量解构赋值的用法

    一.数组赋值(从数组中提取值,按照对应位置,对变量赋值) 1. 完全解构(变量与值数目相等) let arr = [1, 2, 3]; let [a,b,c] = arr; console.log(a ...

  7. es6的解构函数

    话说,解构无处不在啊,鄙人自从用了vue写项目以来,总是遇到各路大神莫名其妙的写法,然并未出任何错,查之,然解构也,呜呼哀哉,进而习之. 解构(Destructuring):是将一个数据结构分解为更小 ...

  8. ES6之解构赋值

    截止到ES6,共有6种声明变量的方法,分别是var .function以及新增的let.const.import和class: 我们通常的赋值方法是: var foo='foo'; function ...

  9. 进军es6(2)---解构赋值

    本该两周之前就该总结的,但最近一直在忙校招实习的事,耽误了很久.目前依然在等待阿里HR面后的结果中...但愿好事多磨!在阿里的某轮面试中面试官问到了es6的掌握情况,说明es6真的是大势所趋,我们更需 ...

随机推荐

  1. linux kernel with param

    Linux kernel support pass param to kernel, this params can be assigned at load time by insmod or mod ...

  2. 《Apache服务用户身份验证管理》RHEL6.3

    1.安装apache软件包 Yum install httpd 2.启动apache服务 /etc/init.d/httpd restart 3.创建一个目录,内编辑一个index.html文件 4. ...

  3. 添加远程链接MySQL的权限

    mysql> grant 权限1,权限2,…权限n on 数据库名称.表名称 to 用户名@用户地址 identified by ‘连接口令’; 权限1,权限2,…权限n代表select,ins ...

  4. 利用PowerDesigner绘制PDM生成SQL Server数据库

    PowerDesigner是个很强大的建模工具,可以利用它绘制各种图形,本文利用该工具绘制PDM,进而生成SQL Server数据库. 比如绘制一个简单的学生选课.教师授课管理系统的PDM: pk表示 ...

  5. 【转】 (C#)利用Aspose.Cells组件导入导出excel文件

    Aspose.Cells组件可以不依赖excel来导入导出excel文件: 导入: public static System.Data.DataTable ReadExcel(String strFi ...

  6. WPF 绑定五(本身就是数据源)

    xaml: <Window x:Class="WpfApplication1.Window5" xmlns="http://schemas.microsoft.co ...

  7. 前端自动化构建工具——gulp

    gulp是基于流的前端自动化构建工具. 一.环境配置 gulp是基于nodejs的,所以没有 nodejs 环境的要先去安装好 然后给系统配上gulp环境 npm install -g gulp 再到 ...

  8. perl编程中的map函数示例

    转自:http://www.jbxue.com/article/14854.html 发布:脚本学堂/Perl  编辑:JB01   2013-12-20 10:20:01  [大 中 小] 本文介绍 ...

  9. js中的数组Array定义与sort方法使用示例

    Array的定义及sort方法使用示例 Array数组相当于java中的ArrayList  定义方法:  1:使用new Array(5  )创建数组 var ary = new Array(5): ...

  10. centos安装mysql(rpm)

    今天安装mysql时候出现错误MySQL conflicts with mysql-4.1.20-2 查看是否安装过mysql rpm -qa mysql 发现安装了老版本的mysql 使用rpm - ...