Back

ES6变量的解构赋值

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

第一次感受过类似的东西应该是在看ks写的NOJ的front utils的时候,项目原先alert框的定义是这个样子的:

function alert(content, title="Notice", icon="information-outline", backdrop = "static") {
    //do something
}

这样子在调用函数的时候大概是这样调用的:

alert('咕咕咕', '欸嘿嘿');

事实证明,没有接触过更好的方法就不会觉得现在的方法不太行,可能是用习惯了,对此也没什么意见甚至觉得这难道不是应该的么。

然后有了几个新的方法,是这么定义的:

function confirm ({
        content="",
        title="Confirm",
        icon="information-outline",
        backdrop="static",
        noText="Cancel",
        yesText="OK"
	}={},
    callback=function(deny){}){
    //do something
}

这样子在调用函数的时候大概是这样调用的:

confirm({
    content: '咕咕咕?',
    noText: "布谷布谷",
    yesText: '咕咕咕'
},deny => {
    //do something
});

这样子在调用函数的时候传入的值和参数的对应关系就很明了了(x

与此相反的是,在写php的时候用到array_search(), strpos()等function的时候总是分不清哪个参数该放哪里,以及参数个数比较多的时候也是一个噩梦,然后当有多个省略参数的时候,比如

function do(parmA, parmB = false, parmC = true){
    //do something
}

如果希望第二个参数使用默认值但是第三个参数使用传入值的话第二个参数就不能留空什么的了(x 虽然这种情况也可以使用php的关联数组来解决,然而这样的话就需要开发者在方法内部进行一些比较复杂的判断。

如果使用前面js的第二种定义方法就不一样了,想使用默认值的直接不写那个key就好了。

当时还不明白这是一种什么写法,只觉得十分的优雅,直到有一次深夜的飞机到了目的地没有地铁在一家遇见小面里面用苏菲go打开了阮一峰老师的ES6入门看到了变量的解构赋值,才意识到了,噢!


常见的操作就是

let [x,y,z] = [a,b,c];

按照顺序对应关系是

x -> a
y -> b
z -> c
  • 允许不完全匹配,就是左边的值可以少于右边的值比如let [x,y] = [a,b,c]则是x->a y->b

  • 允许使用默认值,let [x,y = 'A'] = [a,b]当右边对应的值严格等于undefined的时候,左边的元素会取默认值。以及默认值可以引用其他变量,这个比较绕(x

  • 当右边的模式不是数组的时候,会尝试把右边转化成一个对象,如果成功并且具有Iterator接口就可以匹配,否则会报错(x

  • 支持嵌套比如let [x,[y1,y2],z] = [a,[b1,b2],z]


同样的对象也支持解构赋值,正如前面所说的函数参数的定义那个样子

let { a, b } = { a: 'aaa', b: 'bbb'};

let { a: aaa, b: bbb } = { a: 'aaa', b: 'bbb'}

的简写,阮一峰老师的原文举了比较多的例子,我当时是懵了一下的,左边的模式key和value的变量名傻傻的弄不清楚。

然后短暂的思考后大概理解就是解构赋值实际上是模式匹配,就是匹配左右两边的模式,然后把value对应的赋值给左边的变量。然后在上面的例子里,模式就是对象这个结构以及key,js通过对象里面相同的key来建立左边和右边的关系,然后把建立起来关系的右边的值赋值给左边。或者说 模式就是key?

以及这个模式匹配是可以嵌套的,然后可能就会产生比较复杂的情况了(x

比如阮一峰老师的原文

let obj = {
 p: [
   'Hello',
   { y: 'World' }
 ]
};

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

注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样。

let obj = {
 p: [
   'Hello',
   { y: 'World' }
 ]
};

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

对于后者的情况其实有一些不理解的地方(x

因为前文说过let { a, b } = { a: 'aaa', b: 'bbb'};let { a: aaa, b: bbb } = { a: 'aaa', b: 'bbb'}是一样的,那么let { p, p: [x, { y }] } = obj;是不是应该和let { p: p, p: [x, { y }] } = obj;是一样的,那么这样的话左边的模式就有两个key都是p是撞了的。

然后发现跟我想的不大一样,然后ks给我举了一个栗子

let obj = {
    person : {
        name : 'chen'
    }
};
const { person: { name } } = obj;
console.log(person, name);

然后报错说没有产生person这个变量,然后如果写成const { person, person: { name } }就可以了,然后又回到了之前的问题:这么写不就相当于const {person: person, person: { name } }那么是不是还是装了。。然后xy说解构不会处理模式本身,只处理对应的属性。就是person解构obj,而name解构person。说实话这里我并不是很明白(x

然后我进行了一波注定没有结果的实验,这里就不记录了(x

然后xy又说解构本质上还是引用对象里的属性 obj.p

然后我就冒出了一个猜想,既然左边的模式不把他处理成一个对象的话那是不是就没有键与值的去别的,实际上就只是模式以键的形式进行表达。

对于let {p} = obj的话应该直接let p = obj.p, 对于 let {p : p} 实际上也是let p = obj.p, 但是如果是let {p: [x, { y }]} = obj,则应该是 let [x, { y }] = obj.p会再经过一次解构赋值。

这么想的话好像就没毛病了(x 那就先把猜想放在这里把,或许之后知识面更广了可以再来琢磨这个。


然后文章开头提到的那个函数传参的写法就正是利用了对象的解构赋值。

然后省略了一堆注意点,以下是用途:

  • 交换变量的值 let [x,y] = [1,2],则可以通过[y,x] = [x,y]来实现交换

  • 从函数返回多个值

    let swap = (x,y) => [y,x];
    let [x,y] = swap(233,555);
    
  • 函数参数的默认值 就如文章开头的做法

  • 提取json数据

    let jsonData = {
      id: 42,
      status: "OK",
      data: [867, 5309]
    };
      
    let { id, status, data: number } = jsonData;
    
  • 遍历map解构for (let [key, value] of map)

  • 导入模块的指定方法const { SourceMapConsumer, SourceNode } = require("source-map");