ECMAScript6--变量的解构赋值

变量的解构赋值

数组的解构赋值

基本用法

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这称为解构
以前为变量赋值只能直接指定值:

1
2
3
var a = 1;
var b = 2;
var c = 3;

ES6允许写成这样:

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

可以表示,可以从数组中提取值,按照对应位置,对变量赋值。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同左边的变量就会被赋予对应的值。

1
2
3
4
5
6
7
8
9
let [h,...t] = [1,2,3,4]
h //1
t // [2,3,4]


let [a,s,...d] = [a]
a//a
s//undefined
d//[]

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

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

这两种情况都是属于解构不成功,foo的值都等于undefined。
另一种情况是不完全解构,即等号左边的模式只匹配一部分的等号右边的数组。这种情况下,解构也是可以成功

1
2
3
4
5
let [x,y] = [1,2,3]
// x 1; y 2

let [x,[y],z] = [1,[2,3],4]
// x 1; y 2; z 4

都属于解构不完全但是可以成功。
如果等号的右边不是数组(或者说不是可遍历的解构)那么将会报错

1
2
3
4
5
6
7
//报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

上面的表达式都会报错,因为等号右边的值,要么转为对象以后不具备Iterator接口,要门本身就不具备Iterator接口。
解构赋值不仅适用于var命令,还适用于let和const命令。对于set结构也可以使用数组的解构赋值。

1
let [x,y,z] = new Set([1,2,3])

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

1
2
3
4
5
6
var [foo = true] - [];
foo//true

[x,y = 'b'] = [1] //x=1,y='b'
[x,y = 'b'] = [1,undefined] //x=1,y='b'
[x,y = 'b'] = [1,2] //x=1,y=2

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

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

1
2
3
4
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] = []; //报错

对象的解构赋值

解构不仅可以用于数组,也可以用于对象。

1
2
3
4
5
6
7
8
var {foo,bar} = { foo:'1',bar:'a' }
// foo = '1',bar = 'a'

var {bar,foo} = { foo:'1',bar:'a' }
// foo = '1',bar = 'a'

var { bar } = { foo:'1',bar:'a' }
//bar=undefined

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

如果变量名与属性名不一致,必须写成下面这样。

1
2
3
var { foo: baz } = { foo:'1',bar:'a' }
//baz='1'
//foo undef

这实际上说明,对象的解构赋值是下面形式的简写(参见《对象的扩展》一章)。

1
var { foo: foo, bar: bar } = { foo:'1',bar:'a' };

也就是说,对象的解构赋值的内部机制,是先找到同名属性,真正被赋值的是后者而不是前者。
对象跟数组一样也可以用来嵌套解构赋值。

对象的解构也可以指定默认值。默认值生效的条件是对象的属性值严格等于undefined

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

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

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

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

1
2
3
4
5
6
7
//错误写法
var x
{x} = {x1}


//正确写法
{x} = ({x1})

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

解构赋值允许,等号左边的模式之中,不放置任何变量名。

1
2
3
4
//毫无意义的写法
({} = [true,false])
({} = 123)
({} = [])

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

1
let { log, sin, cos } = Math

会将Math对象的对数、预先】正弦三个方法赋值到对应的变量上。

字符串的解构赋值

字符串也可以解构赋值,这是因为字符串被转换成了一个类似数组的对象

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

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

1
2
let { length: lem} ='hello'
len //5

数值和布尔值的解构赋值

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

1
2
3
4
5
let {toString: s} = 123;
s=== Number.prototype.toString //true

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

由于数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。
解构赋值的规则时,只要等号右边的值不是对象,就先将其转换为对象,由于undefined和null无法转换为对象,所以对他们进行解构赋值都会报错。

函数参数的结构辅助

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

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

函数add的参数实际上不是一个数组,而是通过解构得到的变量x和y。

1
2
[[1,2],[3,4]].map(([a,b]) => a+b)
//[3,7]

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

圆括号问题

ES6的规则是只要有可能导致解构的歧义,就不得使用圆括号。

以下三种解构赋值不得使用圆括号

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

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

2、函数参数中,模式不能带有圆括号
函数参数也属于变量声明,因此不能带有圆括号

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

3、不能将整个模式,或嵌套模式中的一层,放在圆括号这

1
2
({p:a}) = {p:42}
{[a]} = [5]

可以使用圆括号的情况

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

1
2
[(b)] = [3]
({p:(d)} = {})

赋值语句而不是声明语句,赋值语句可以执行,声明语句不可以执行。

用途

  • 交换变量的值 [x,y] = [y,x],代码简洁易读语义清晰
  • 从函数返回多个值。
    函数只能返回一个值,如果要返回多个值,只能将他们放在数组或者对象里返回。有了解构赋值,取出这些值就非常方便。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //返回一个数组
    function example(){
    return [1,2,3]
    }

    //返回一个对象
    function example(){
    return {
    foo: 1,
    bar: 2
    }
    }
  • 函数参数的定义
    解构赋值可以方便的将一组参数与变量名对应起来
    1
    2
    3
    4
    5
    6
    7
    //参数是一组有次序的值
    function f([x,y,z]){...}
    f([1,2,3])

    //参数是一组无次序的值
    function f({x,y,z}){...}
    f({z:1,x:2,y:3})
  • 提取JSON数据
  • 函数参数的默认值
  • 遍历Map解构
    任何部署可iterator接口的对象,都可以用for…of循环遍历。Map结构原生支持Iterator接口,配合变量的解构赋值,获取键名和键值就非常方便。
  • 输入模块的指定方法
    加载模块时,往往需要指定哪些输入方法,解构赋值使得输入语句非常清晰。const { s,s1 } = require('xxx')
-------------本文结束感谢您的阅读-------------