解构赋值:从数组和对象中提取值,对变量进行赋值。

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


1、数组的结解构赋值

基本用法

let [foo, [[bar], baz]] = [1,[[2], 3]];
foo //
bar //
baz //
let[ , , third] = [1,2,3];
third //
let[head, ...tail] = [1,2,3,4];
head //
tail // [2,3,4]

解构不成功

不完全解构:等号左边的模式只匹配等号右边数组的一部分。 这种情况下解构依然可以成功。

let[x,y,...z] = ['a']
x // 'a'
y // undefined ____解构不成功,变量的值就等于undefined____
z // [] ____rest参数搭配的变量是一个数组____
let [a, [b], d] = [1, [2, 3] ,4];
a //
b //
d //

若等号右边为不可遍历的结构,将会报错;   解构赋值适用于var、let、const命令

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

前五个转为对象之后不具备Iterator接口【遍历器接口】, 最后一个不具备Iterator接口【遍历器接口】

只要某种数据结构具有Iterator接口,都可以采用数组的形式解构赋值。例如Set结构、Generator函数

let [x, y, z] = new Set([ "a", "b", "c" ]);
x // a
function* fibs() {
var a = 0;
var b = 1;
while(true) {
yield a;
[a, b] = [b, a+b];    //[0,1] [1,1] [1,2] [2,3] [3,5] [5,8]
}
}
var [first, second, third, three, four, five, six] = fibs(); // 数组形式的解构赋值 six //

默认值

解构赋值允许使用默认值

[ 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

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

function f() {
console.log(111);
}
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 ] = [];    //报错

2、对象的解构赋值

对象的解构赋值与数组的解构赋值最重要的不同: 数组元素是按顺序排列的,变量的取值由位置决定; 而对象的属性没有次序,变量必须与属性同名。

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

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

var {bar, foo} = {foo:'aaa', bar:'bbb'};
等价于
var {bar:bar, foo:foo} = {foo:'aaa', bar:'bbb'};
var {foo: baz} = {foo:'aaa', bar:'bbb'};
(模式)(变量) (属性)(属性值) //通过模式找到同名的属性,然后将属性值赋值给对应的变量。
baz // 'aaa'
foo // foo is not defined 报错

这种写法的变量的声明和赋值是一体的;因此对于let和const,变量不能重新声明,否则会报错。

let foo;
let {foo} = {foo:1} // 报错 foo被重复声明 let baz;
let {bar: baz} = {bar: 1} // 报错 baz被重复声明

若没有第二个let ,则不会报错;

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

嵌套的对象也可以实现解构赋值

const node = {
loc: {
start: {
line: 1,
column: 5
}
}
}; let { loc, loc: { start }, loc: { start: { line }} } = node;
line //
loc // Object {start: Object}
start // Object {line: 1, column: 5}

上面代码 分别是对locstartline三个属性的解构赋值;

最后一次对line 属性的解构赋值中,只有line是变量,loc和start都是模式。。。

对象的结构也可以指定默认值; 默认值生效的条件——对象的属性值严格等于(===)undefinend

var {x=3} = {};                    // x=3
var {x, y=5} = {x:1};    // x=1;y=5
var {message:msg = 'huhuh' } = {} // msg='huhuhu' 其中message为模式

解构失败,变量的值为undefined

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

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

var x;
{x} = {x:1}; // 报错

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

将整个解构赋值语句放在一个圆括号内,就能解决这个问题。

var x;
({x} = {x:1}); // 正常

解构赋值允许左边的模式中,不放置任何的变量名;因此可以写出非常古怪的表达式。(表达式没有意义,但是能够正常执行)

({} = [true, false]);
({} = 'abc');
({} = []);

对象的解构赋值能将现有对象的方法,赋值到某个变量;

let { log, sin, cos } = Math;

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

由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构赋值

let arr = [1,2,3];
let { 0: first, [arr.length-1]:last } = arr; //其中方括号这种写法属于“属性名表达式”
first //
last //

3、字符串的解构赋值

因为字符串可以被转换为一个类似数组的对象,所以字符串也可以进行解构赋值

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

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

let {length : len} = 'hello';          等价于 {length : length} = {length : 5 , //还有其他的属性 , ……}
len // 5

4、数值和布尔值的解构赋值

解构赋值时,等号右边是数值或者布尔值,则先转化为对象。

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

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

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

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

5、函数参数的解构赋值

函数add的参数表面上是一个数组,但是在传入的那一刻,数组参数就被解构为变量x 和 y。函数内部接收到的就是变量x 和 y。

function add([x, y]) {
return x+y;
}
add([, ]); [[1,2],[3,4]].map( ([a,b]) => a+b ); //[3,7]

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

以下函数为变量x、y指定默认值。

function move( {x=0,y=0} = {} ) {                 等价于 -- {x:x=0, y:y=0} = {}
return [x, y];
}
move( {x:3, y:8} );    // [3,8]          {x:x=0, y:y=0} = {x:3, y:8}
move( {x:3 ); // [3,0] {x:x=0, y:y=0} = {x:3}
move( {} ); // [0,0] {x:x=0, y:y=0} = {}
move(); // [0,0]           没有结构成功的情况下,x y 等于默认值!!

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

function move( {x, y} = { x:0, y:0 } ) {        等价于 -- {x:x, y:y} = {}
return [x, y];
}
move( {x:3, y:8} );    // [3,8]
move( {x:3 ); // [3,undefined] {x:x, y:y} = {x:3, y:undefined}
move( {} ); // [undefined,undefined] {x:x, y:y} = {x:undefined, y:undefined}
move(); // [0,0] {x:x, y:y} = {x:0, y:0} 函数不传参的情况下,参数的默认值才生效

以上代码是为函数move的参数指定默认值, 而不是为变量x、y指定默认值。

undefined会触发函数参数的默认值。

[1,undefined,3].map( (x='yes') => x )     // [1,yes,3]

6、圆括号的问题

解构赋值虽然很方便,但是解析并不容易;对于编译器来说,一个式子是模式还是表达式,只有解析到(解析不到)等号才知道。

ES6中对于圆括号的规定:只要可能导致结构歧义,就不得使用圆括号。---- 只要有可能就不要在模式中放置圆括号

不能使用圆括号的三种情况:

(1)变量声明语句中,模式不能带有圆括号。

var [(a)] = [1];
let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};
var { o: ({p:p}) } = { o:{p:2} }
//以上全部报错,因为他们都是变量声明语句,模式不能带有圆括号

(2)函数参数中,模式不能带有圆括号。 ---- 函数参数也属于变量声明

function f( [(z)] ) { return z; }

(3)不能将整个模式或者嵌套模式中的一层放在圆括号中。

( {p: a} ) = { p: 23 }
( [a] ) = [5]
// 以上整个模式放入括号,导致全部报错 [ ({p: a}), {x: c} ] = [{}, {}]
// 嵌套模式中的一层放入括号, 导致报错

可以使用圆括号的情况只有一种: 赋值语句的非模式部分可以使用圆括号。

[ (b) ] = [3];             // 此语句模式是取数组的第一个元素,与圆括号无关
( { p: (d) } = {} );    // 此语句模式是p
[ (parseInt.prop) ] = [3]; //以上语句都可以正常执行, (1)他们是赋值语句; (2)他们的圆括号都不属于模式的一部分;

7、解构赋值的用途

(1)交换变量的值

[x, y] = [y, x]

(2)取出函数返回的多个值

函数只能返回一个值,若要返回多个值,只能将其放在数组或者对象中返回。

function example() {
return [1,2,3];
}
var [a,b,c] = 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: 23,
status: "ok",
data: [111, 222]
}
let { id, status, data:number } = jsonData; id //
status // ok
number // [111, 222]

(5)函数参数默认值

(6)遍历Map结构

任何部署了Iterator接口的对象,都可以用for..of..循环遍历。

var map = new Map();
map.set('first', 'hello');
map.ser('second, 'world'); for(let [key, value] of map) {
console.log('key' is 'value');
} // first is hello
// second is world

(7)输入模块的指定方法

加载模块时,往往需要指定输入哪些方法,解构赋值使输入非常的清晰。

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

ES6标准入门 第三章:变量的解构赋值的更多相关文章

  1. ES6 第三章 变量的解构赋值 具体参照http://es6.ruanyifeng.com

    1.基本用法 let [a, b, c] = [1, 2, 3];左右两边解构格式要保持一致. 2.默认值 let [x, y = 'b'] = ['a']; // x='a', y='b' let ...

  2. ES6学习 第二章 变量的解构赋值

    前言 该篇笔记是第二篇 变量的解构赋值. 这一章原文链接: 变量的解构赋值 解构赋值 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). 解构 ...

  3. ES6学习笔记(一)-变量的解构赋值

    变量的解构赋值种类 解构(Destructuring):ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值. 只有当一个数组成员严格等于(===)undefined,包括空“ ” ,默认值 ...

  4. ES6知识整理(2)--变量的解构赋值

    最近准备在业余空闲时间里一边学习ES6,一边整理相关知识.只有整理过的学习才是有效的学习.也就是学习之后要使用和整理成文,才是真正的学到了... 上一篇是一个试水,现在接上. 变量提升 看了下朋友回复 ...

  5. ES6学习笔记(let,const,变量的解构赋值)

    1.let: ; i < 3; i++) { let i = 'abc'; console.log(i); } // abc // abc // abc 不存在变量提升,它所声明的变量一定要在声 ...

  6. 《ES6标准入门》(阮一峰)--3.变量的解构赋值

    1.数组的解构赋值 基本用法 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). 以前,为变量赋值,只能直接指定值. let a = 1; l ...

  7. ES6入门——变量的解构赋值

    1.数组的解构赋值 以前为变量复制,只能直接指定值.现在ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). 本质上,这种写法属于模式匹配,只要等 ...

  8. ES6学习之变量的解构赋值

    前言:什么是ES6?ECMAScript 6(简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了.其中相比较于ES5新增了诸多的特性,并且ES6可转换为ES5的语法.- ...

  9. ES6的新特性(3)——变量的解构赋值

    变量的解构赋值 数组的解构赋值 基本用法 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). let a = 1; let b = 2; le ...

随机推荐

  1. imagecreatefromjpeg(): gd-jpeg: JPEG library reports unrecoverable

    错误: imagecreatefromstring(): Empty string or invalid image 或者 imagesx() expects parameter 1 to be re ...

  2. C语言实现栈代码

    /* 栈的特性:先进后出. 栈在计算语言处理和将递归算法改为非递归算法等方面起着非常重要的作用. */ #define INITSIZE 100 //储存空间的初始分配量 typedef int El ...

  3. php:页面乱码的解决方法

    在 <?php header("Content-Type:text/html;charset=utf-8"); ////设置页面显示的文字编码 头部就写header函数处理成 ...

  4. 约会 Rendezvous:基环树

    提炼:tarjan判环,dfs建树,倍增lca,预处理环两点间距离 我犯的错误: 1.基环树不只有一棵,可以有很多 2.自环不能将其忽略,(对于我的算法)应该将其特殊考虑在算法内 3.代码一定要简洁有 ...

  5. redis主从+keepalived实现高可用技术

    Redis是我们当下比较流行使用的非关系数据库,可支持多样化的数据类型,多线程高并发支持,redis运行在内存拥有更快的读写.因为redis的表现如此出色,如何能保障redis在运行中能够应对宕机故障 ...

  6. IP地址(参考百度百科)

    题目1:用子网掩码划分网络时,如果划分8个子网,则需要4位.因为24-2>=8.(全0代表网络自身,全1代表广播地址,所以减2) 以C类地址为例,(1)8个子网,则4位子网,主机号为后4位,主机 ...

  7. 利用aspose-words直接将Word转化为图片

    之前遇到一个需求,需要在word文档中加入一些文字,并转化为图片.之前也试过几种方案,但是发现效果还不是很理想,且中间需要经过一次转化为pdf的过程,最近找到了最理想的方式,即利用aspose-wor ...

  8. shiro框架学习-8-shiro缓存

    1. shiro进行认证授权时会查询数据库获取用户角色权限信息,每次登录都会去查询,这样对性能会又影响.可以设置缓存,查询时先去缓存中查找,缓存中没有再去数据库查询. 从shiro的架构图中可以看到有 ...

  9. 为什么要重写hashcode( )和equals( )?

    打个比方,一个名叫张三的人去住酒店,在前台登记完名字就去了99层100号房间,此时警察来前台找叫张三的这个人住在哪间房,经过查询,该酒店住宿的有50个叫张三的,需要遍历查询,查询起来很不方便. 那么就 ...

  10. lookup-method和replace-method注入

    官方文档说明: 在Spring5核心的1.4.6章节 在大多数应用程序方案中,容器中的大多数bean都是 singletons .当单例bean需要与另一个单例bean协作或非单例bean需要与另一个 ...