ECMAScript6变量的解构赋值实例详解


Posted in Javascript onSeptember 19, 2017

数组的解构赋值

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)

var [a, b, c] = [1, 2, 3];

这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

下面是一些使用嵌套数组进行解构的例子

let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

如果解构不成功,变量的值就等于 undefined

foo 的值都会等于 undefined

var [foo] = [];
var [bar, foo] = [1];

不完全解构即等号左边的模式,只匹配一部分的等号右边的数组

let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4

如果等号的右边不是数组,那么将会报错。

// 报错let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

解构赋值不仅适用于var命令,也适用于let和const命令

var [v1, v2, ..., vN ] = array;
let [v1, v2, ..., vN ] = array;
const [v1, v2, ..., vN ] = array;

对于Set结构,也可以使用数组的解构赋值。

let [x, y, z] = new Set(["a", "b", "c"]);
x // "a"

只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值

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();
sixth // 5

fibs 是一个Generator函数,原生具有Iterator接口。解构赋值会依次从这个接口获取值

解构赋值允许指定默认值。

var [foo = true] = [];
foo // true
[x, y = 'b'] = ['a']; // x='a', y='b'
[x, y = 'b'] = ['a', undefined]; // x='a', y='b'

ES6内部使用严格相等运算符( === ),判断一个位置是否有值。所以,如果一个数组成员不严格等于 undefined ,默认值是不会生效的。

var [x = 1] = [undefined];
x // 1
var [x = 1] = [null];
x // null

如果一个数组成员是 null ,默认值就不会生效,因为 null 不严格等于 undefined

function f() {
console.log('aaa');
}
let [x = f()] = [1];
//等价于
let x;
if ([1][0] === undefined) {
  x = f();
} else {
  x = [1][0];
}

如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值

默认值可以引用解构赋值的其他变量,但该变量必须已经声明

let [x = 1, y = x] = []; // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; // ReferenceError

是因为 x 用到默认值 y 时, y 还没有声明

对象的解构赋值

var { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值

var { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
var { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined

实际上,对象的解构赋值是下面形式的简写

var { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };

对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者

var { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined

上面代码中,真正被赋值的是变量 baz ,而不是模式 foo

变量的声明和赋值是一体的。对于let和const来说,变量不能重新声明,所以一旦赋值的变量以前声明
过,就会报错

let foo;
let {foo} = {foo: 1}; 
// SyntaxError: Duplicate declaration "foo"
let baz;
let {bar: baz} = {bar: 1}; 
// SyntaxError: Duplicate declaration "baz"

因为 var 命令允许重新声明,所以这个错误只会在使用 let 和 const 命令时出现。如果没有第二个let命令,上面的代码就不会报错

let foo;
({foo} = {foo: 1}); // 成功
let baz;
({bar: baz} = {bar: 1}); // 成功

和数组一样,解构也可以用于嵌套结构的对象

var obj = {
  p: [
   "Hello",
   { y: "World" }
  ]
};
var { p: [x, { y }] } = obj;
x // "Hello"
y // "World"

这时 p 是模式,不是变量,因此不会被赋值

var node = {
  loc: {
   start: {
     line: 1,
     column: 5
   }
  }
};
var { loc: { start: { line }} } = node;
line // 1
loc // error: loc is undefined
start // error: start is undefined

只有 line 是变量, loc 和 start 都是模式,不会被赋值

嵌套赋值的例子。

let obj = {};
let arr = [];
({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
obj // {prop:123}
arr // [true]

对象的解构也指定默认值

var {x = 3} = {};
x // 3
var {x, y = 5} = {x: 1};
x // 1
y // 5
var { message: msg = "Something went wrong" } = {};
msg // "Something went wrong"

默认值生效的条件是,对象的属性值严格等于 undefined

var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null

如果解构失败,变量的值等于 undefined

var {foo} = {bar: 'baz'};
foo // undefined

解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错

// 报错
var {foo: {bar}} = {baz: 'baz'};

等号左边对象的 foo 属性,对应一个子对象。该子对象的 bar 属性,解构时会报错。因为 foo 这时等于 undefined ,再取子属性就会报错

要将一个已经声明的变量用于解构赋值,必须非常小心

// 错误的写法
var x;
{x} = {x: 1};
// SyntaxError: syntax error
// 正确的写法
({x} = {x: 1});

因为JavaScript引擎会将 {x} 理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免JavaScript将其解释为代码块,才能解决这个问题

对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量

let { log, sin, cos } = Math;

将 Math 对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多

字符串的解构赋值

字符串也可以解构赋值。此时字符串被转换成了一个类似数组的对象

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

类似数组的对象都有一个 length 属性,因此还可以对这个属性解构赋值

let {length : len} = 'hello';
len // 5

数值和布尔值的解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象

let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true

数值和布尔值的包装对象都有 toString 属性,因此变量 s 都能取到值

解构赋值的规则是,只要等号右边的值不是对象,就先将其转为对象。由于 undefined 和 null 无法转为对象,所以对它们进行解构赋值,都会报错。

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

函数参数的解构赋值

function add([x, y]){
  return x + y;
}
add([1, 2]); // 3

函数参数的解构也可以使用默认值

function move({x = 0, y = 0} = {}) {
  return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

函数 move 的参数是一个对象,通过对这个对象进行解构,得到变量 x 和 y 的值。如果解构失败, x 和 y 等于默认值

function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

是为函数 move 的参数指定默认值,而不是为变量 x 和 y 指定默认值,所以会得到与前一种写法不同的结果。undefined 就会触发函数参数的默认值

圆括号问题

解构赋值虽然很方便,但是解析起来并不容易。对于编译器来说,一个式子到底是模式,还是表达式,没有办法从一开始就知道,必须解析到(或解析不到)等号才能知道如果模式中出现圆括号怎么处理。ES6的规则是,只要有可能导致解构的歧义,就不得使用圆括号。但是,这条规则实际上不那么容易辨别,处理起来相当麻烦。因此,建议只要有可能,就不要在模式中放置圆括号

不能使用圆括号的情况

1.变量声明语句中,不能带有圆括号

// 全部报错
var [(a)] = [1];
var {x: (c)} = {};
var ({x: c}) = {};
var {(x: c)} = {};
var {(x): c} = {};}
var { o: ({ p: p }) } = { o: { p: 2 } };

2.函数参数中不能使用圆括号

// 报错
function f([(z)]) { return z; }

3.赋值语句中,不能将整个模式,或嵌

套模式中的一层,放在圆括号之中

将整个模式放在模式之中,导致报错

// 全部报错
({ p: a }) = { p: 42 };
([a]) = [5];

将嵌套模式的一层,放在圆括号之中,导致报错

[({ p: a }), { x: c }] = [{}, {}];

可以使用圆括号的况

赋值语句的非模式部分,可以使用圆括号

[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确

首先它们都是赋值语句,而不是声明语句;其次它们的圆括号都不属于模式的一部分。第一行语句中,模式是取数组的第一个成员,跟圆括号无关;第二行语句中,模式是p,而不是d;第三行语句与第一行语句的性
质一致

用途

1.交换变量的值

[x, y] = [y, x];

2.从函数返回多个值

// 返回一个数组
function example() {
  return [1, 2, 3];
}
var [a, b, c] = example();
// 返回一个对象
function example() {
  return {
   foo: 1,
   bar: 2
  };
}
var { foo, bar } = example();

3.函数参数的定义

解构赋值可以方便地将一组参数与变量名对应起来

function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});

4.提取JSON数据

var jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]

5.函数参数的默认值

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

6.便利Map结构

var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
  console.log(key + " is " + value);
}
// first is hello
// second is world
// 获取键名
for (let [key] of map) {
// ...
}
// 获取键值
for (let [,value] of map) {
// ...
}

7.输入模块的指定方法

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

总结

以上所述是小编给大家介绍的使用ECMAScript6变量的解构赋值实例详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
Jquery 插件学习实例1 插件制作说明与tableUI优化
Apr 02 Javascript
基于jquery的loading效果实现代码
Nov 05 Javascript
jQuery父级以及同级元素查找介绍
Sep 04 Javascript
IE8中动态创建script标签onload无效的解决方法
Dec 22 Javascript
在Node.js应用中使用Redis的方法简介
Jun 24 Javascript
利用jQuery插件imgAreaSelect实现图片上传裁剪(同步显示图像位置信息)
Dec 02 Javascript
详解vue 中使用 AJAX获取数据的方法
Jan 18 Javascript
浅谈关于angularJs中使用$.ajax的注意点
Aug 12 Javascript
jQuery实现的下雪动画效果示例【附源码下载】
Feb 02 jQuery
Angular如何由模板生成DOM树的方法
Dec 23 Javascript
JavaScript实现通讯录功能
Dec 27 Javascript
Html5生成验证码的示例代码
May 10 Javascript
vue 粒子特效的示例代码
Sep 19 #Javascript
jQuery实现简单日期格式化功能示例
Sep 19 #jQuery
慕课网题目之js实现抽奖系统功能
Sep 19 #Javascript
使用vue与jquery实时监听用户输入状态的操作代码
Sep 19 #jQuery
JavaScript事件处理程序详解
Sep 19 #Javascript
jQuery选择器之属性筛选选择器用法详解
Sep 19 #jQuery
vue小图标favicon不显示的解决方案
Sep 19 #Javascript
You might like
PHP的简易冒泡法代码分享
2012/08/28 PHP
PHP单文件上传原理及上传函数的封装操作示例
2019/09/02 PHP
载入进度条 效果
2006/07/08 Javascript
prototype1.4中文手册
2006/09/22 Javascript
js setTimeout 常见问题小结
2013/08/13 Javascript
js this函数调用无需再次抓获id,name或标签名
2014/03/03 Javascript
Javascript实现div的toggle效果实例分析
2015/06/09 Javascript
浅析JS异步加载进度条
2016/05/05 Javascript
jquery 无限极下拉菜单的简单实例(精简浓缩版)
2016/05/31 Javascript
用jquery的attr方法实现图片切换效果
2017/02/05 Javascript
nodeJS(express4.x)+vue(vue-cli)构建前后端分离实例(带跨域)
2017/07/05 NodeJs
360提示[高危]使用存在漏洞的JQuery版本的解决方法
2017/10/27 jQuery
Angular ElementRef简介及其使用
2018/10/01 Javascript
vue实现微信二次分享以及自定义分享的示例
2019/03/20 Javascript
微信小程序动态添加view组件的实例代码
2019/05/23 Javascript
详解vue中的父子传值双向绑定及数据更新问题
2019/06/13 Javascript
Node.js之删除文件夹(含递归删除)代码实例
2019/09/09 Javascript
在Django中使用Sitemap的方法讲解
2015/07/22 Python
深入浅出学习python装饰器
2017/09/29 Python
python3+PyQt5泛型委托详解
2018/04/24 Python
Python实现的爬取网易动态评论操作示例
2018/06/06 Python
tensorflow 获取变量&打印权值的实例讲解
2018/06/14 Python
通过python顺序修改文件名字的方法
2018/07/11 Python
对python中的 os.mkdir和os.mkdirs详解
2018/10/16 Python
Django数据库连接丢失问题的解决方法
2018/12/29 Python
使用Python实现图像标记点的坐标输出功能
2019/08/14 Python
python错误调试及单元文档测试过程解析
2019/12/19 Python
python3通过udp实现组播数据的发送和接收操作
2020/05/05 Python
Stuart Weitzman美国官网:美国奢华鞋履品牌
2016/08/18 全球购物
英国手工布艺沙发在线购买:Sofas & Stuff
2018/03/02 全球购物
一些关于MySql加速和优化的面试题
2014/01/30 面试题
人事主管岗位职责
2015/02/04 职场文书
幼儿园教师自荐书
2015/03/06 职场文书
Spring Security使用单点登录的权限功能
2022/04/03 Java/Android
MySQL数据库安装方法与图形化管理工具介绍
2022/05/30 MySQL
详解SQL报错盲注
2022/07/23 SQL Server