解析JavaScript的ES6版本中的解构赋值


Posted in Javascript onJuly 28, 2015

什么是解构赋值?

解构赋值允许你使用类似数组或对象字面量的语法将数组和对象的属性值赋给一系列变量。这个语法非常简洁,而且比传统的属性访问更加清晰。

在不使用解构赋值的情况下,访问数组的前三项:

var first = someArray[0];
var second = someArray[1];
var third = someArray[2];
 
var first = someArray[0];
var second = someArray[1];
var third = someArray[2];

使用解构赋值后,相应的代码变得更简洁和可读:

var [first, second, third] = someArray;
 
var [first, second, third] = someArray;

SpiderMonkey(Firefox 的 JavaScript 引擎)已经支持解构赋值的大部分特性,但还不完全。
数组和可迭代对象的解构赋值

上面我们已经看到了数组解构赋值的例子,该语法的一般形式是:

[ variable1, variable2, ..., variableN ] = array;
 
[ 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 [ 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 [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 [,,third] = ["foo", "bar", "baz"];
console.log(third);
// "baz"

你还可以用一个 Rest 表达式来捕获数组中的剩余项:

var [head, ...tail] = [1, 2, 3, 4];
console.log(tail);
// [2, 3, 4]
 
var [head, ...tail] = [1, 2, 3, 4];
console.log(tail);
// [2, 3, 4]

如果数组越界或访问数组中不存在的项,将得到和通过数组索引访问一样的值:undefined。

console.log([][0]);
// undefined

var [missing] = [];
console.log(missing);
// 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
 
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 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 { 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"
 
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
 
var { missing } = {};
console.log(missing);
// undefined

使用对象的解构赋值时还有一个潜在的陷阱,在解构赋值时没有声明变量(没有 var、let或 const 关键字):

{ blowUp } = { blowUp: 10 };
// Syntax error
 
{ blowUp } = { blowUp: 10 };
// Syntax error

这是因为 JavaScript 语法告诉引擎任何以 { 开始的语句都是语句块(例如,{console} 就是一个合法的语句块),解决方法是将整个语句用一对括号包裹:

({ safe } = {});
// No errors
 
({ safe } = {});
// No errors

其他情况

当你尝试解构 null 或 undefined,你将得到类型错误:

var {blowUp} = null;
// TypeError: null has no properties
 
var {blowUp} = null;
// TypeError: null has no properties

不过,你可以对其他基本类型(Boolean、String 和 Number)进行解构,将得到 undefined:

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

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

结果也许会让你感到意外,但深究一下,其实原因很简单。在进行对象解构赋值时,被解构的对象将被强制转换为 Object,除 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
 
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

实际应用
函数参数

作为开发人员,我们经常把一个包含多个属性的对象作为函数的参数,来实现更灵活的 API,而不是让 API 的使用者记住一些特定顺序的参数。我们可以使用对象的解构赋值,来避免每次使用参数时的属性访问:

function removeBreakpoint({ url, line, column }) {
 // ...
}
 
function removeBreakpoint({ url, line, column }) {
 // ...
}

配置对象

完善上面的例子,我们可以为要被解构的对象属性提供默认值,这在对那些作为配置参数的对象非常实用,因为许多配置项都有一个合理的默认值。例如,jQuery 的 ajax 方法的第二个参数为一个配置对象,我们可以这样实现:

jQuery.ajax = function (url, {
 async = true,
 beforeSend = noop,
 cache = true,
 complete = noop,
 crossDomain = false,
 global = true,
 // ... more config
}) {
 // ... do stuff
};
 
jQuery.ajax = function (url, {
 async = true,
 beforeSend = noop,
 cache = true,
 complete = noop,
 crossDomain = false,
 global = true,
 // ... more config
}) {
 // ... do stuff
};

这避免了类似这样的重复代码:var foo = config.foo || theDefaultFoo;。
与迭代器一起使用

当遍历 Map 对象时,我们可以使用解构赋值来遍历 [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"

 
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 [key] of map) {
 // ...
}

只遍历值:
for (var [,value] of map) {
 // ...
}

 
for (var [,value] of map) {
 // ...
}

返回多个值

返回一个数组,通过解构赋值提取到返回值:

function returnMultipleValues() {
 return [1, 2];
}
var [foo, bar] = returnMultipleValues();

 
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 { foo, bar } = returnMultipleValues();

这两者都比使用中间变量好:

function returnMultipleValues() {
 return {
  foo: 1,
  bar: 2
 };
}
var temp = returnMultipleValues();
var foo = temp.foo;
var bar = temp.bar;
 
function returnMultipleValues() {
 return {
  foo: 1,
  bar: 2
 };
}
var temp = returnMultipleValues();
var foo = temp.foo;
var bar = temp.bar;

采用延续式:

function returnMultipleValues(k) {
 k(1, 2);
}
returnMultipleValues((foo, bar) => ...);

 
function returnMultipleValues(k) {
 k(1, 2);
}
returnMultipleValues((foo, bar) => ...);

导入 CommonJS 模块的指定部分

还没使用过 ES6 的模块吧,那至少使用过 CommonJS 吧。当导入一个 CommonJS 模块 X 时,模块提供的方法也许多余你实际使用的。使用解构赋值,你可以明确指定你需要使用模块的哪些部分:

const { SourceMapConsumer, SourceNode } = require("source-map");
 
const { SourceMapConsumer, SourceNode } = require("source-map");

如果你使用 ES6 的模块机制,你可以看到 import 声明时有一个类似的语法。
结论

我们看到,解构赋值在很多场景下都很实用。在 Mozilla,我们已经有很多经验。Lars Hansen 在 10 年前就向 Opera 引入了解构赋值,Brendan Eich 在稍微晚点也给 Firefox 添加了支持,最早出现在 Firefox 2 中。因此,解构赋值已经渗透到我们每天对 JS 的使用中,悄悄地使我们的代码更简短、整洁。

Javascript 相关文章推荐
基于jquery的一个浮动框(扩展性比较好 )
Aug 27 Javascript
Js日期选择自动填充到输入框(界面漂亮兼容火狐)
Aug 02 Javascript
iframe子父页面调用js函数示例
Nov 07 Javascript
JS实现图片产生波纹一样flash效果的方法
Feb 27 Javascript
详谈javascript异步编程
Feb 21 Javascript
js老生常谈之this,constructor ,prototype全面解析
Apr 05 Javascript
通过javascript进行UTF-8编码的实现方法
Jun 27 Javascript
angular2倒计时组件使用详解
Jan 12 Javascript
jQuery实现别踩白块儿网页版小游戏
Jan 18 Javascript
动态创建Angular组件实现popup弹窗功能
Sep 15 Javascript
JS实现的计数排序与基数排序算法示例
Dec 04 Javascript
VUE页面中通过双击实现复制表格中内容的示例代码
Jun 11 Javascript
深入学习JavaScript中的Rest参数和参数默认值
Jul 28 #Javascript
JQuery实现鼠标滚轮滑动到页面节点
Jul 28 #Javascript
CSS3实现动态背景登录框的代码
Jul 28 #Javascript
javascript制作幻灯片(360度全景图片)
Jul 28 #Javascript
详解JavaScript ES6中的模板字符串
Jul 28 #Javascript
javascript解决IE6下hover问题的方法
Jul 28 #Javascript
JavaScript如何自定义trim方法
Jul 28 #Javascript
You might like
自己前几天写的无限分类类
2007/02/14 PHP
php文件服务实现虚拟挂载其他目录示例
2014/04/17 PHP
php获取从html表单传递数组的方法
2015/03/20 PHP
PHP 无限级分类
2017/05/04 PHP
Jquery Validate 正则表达式实用验证代码大全
2013/08/23 Javascript
通过url查找a元素并点击
2014/04/09 Javascript
使用jQuery中的when实现多个AJAX请求对应单个回调的例子分享
2014/04/23 Javascript
整理Javascript流程控制语句学习笔记
2015/11/29 Javascript
Jquery组件easyUi实现表单验证示例
2016/08/23 Javascript
jQuery遍历节点树方法分析
2016/09/08 Javascript
浅谈javascript的闭包
2017/01/23 Javascript
js 转义字符及URI编码详解
2017/02/28 Javascript
JavaSctit 利用FileReader和滤镜上传图片预览功能
2017/09/05 Javascript
微信小程序实现文字跑马灯效果
2020/05/26 Javascript
原生JS forEach()和map()遍历的区别、兼容写法及jQuery $.each、$.map遍历操作
2019/02/27 jQuery
微信小程序项目总结之记账小程序功能的实现(包括后端)
2019/08/20 Javascript
BootstrapValidator实现表单验证功能
2019/11/08 Javascript
使用Bootstrap做一个朝代历史表
2019/12/10 Javascript
TypeScript 引用资源文件后提示找不到的异常处理技巧
2020/07/15 Javascript
[07:09]DOTA2-DPC中国联赛 正赛 Ehome vs Elephant 选手采访
2021/03/11 DOTA
python获取网页中所有图片并筛选指定分辨率的方法
2018/03/31 Python
Python爬虫实现(伪)球迷速成
2018/06/10 Python
python使用xlsxwriter实现有向无环图到Excel的转换
2018/12/12 Python
pytorch中tensor张量数据类型的转化方式
2019/12/31 Python
Python列表如何更新值
2020/05/27 Python
Python 分布式缓存之Reids数据类型操作详解
2020/06/24 Python
浅谈优化Django ORM中的性能问题
2020/07/09 Python
python 无损批量压缩图片(支持保留图片信息)的示例
2020/09/22 Python
Python开发.exe小工具的详细步骤
2021/01/27 Python
意大利体育用品网上商城:Nencini Sport
2016/08/18 全球购物
龟牌英国商店:Turtle Wax Brand Store UK
2019/07/02 全球购物
出国签证在职证明
2014/01/16 职场文书
党员活动日总结
2014/05/05 职场文书
会计学专业求职信
2014/07/17 职场文书
低碳环保演讲稿
2014/08/28 职场文书
生产现场禁烟通知
2015/04/23 职场文书