ES6标准入门(三)之变量的解构赋值

536 阅读5分钟

数组的结构赋值

1.基本用法

ES6允许按照一定模式从数组和对象中提取值,然后对变量进行赋值,被称为 解构赋值
曾经,我们是这样赋值的:

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

现在,我们升级了,可以这么写!

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

↑ 这段代码表示我们可以从数组中提取值,给对应位置变量赋值。
本质上这种写法属于 匹配模式,只要等号两边的模式相同,左边的变量就会被赋予对应位置的值,下面举一些栗子:

  1. 完全解构栗子
    let [a,[b],c] = [1,[2],3];
    console.log(a,b,c); // 1 2 3 
    
    let [x,,y] = [1,2,3];
    console.log(x,y); // 1 3
    
    let [head,...tail] = [1,2,3,4];
    console.log(head,tail); // 1 [2,3,4]
    
    let [x,y,...z] = ['a'];
    console.log(x,y,z); // 'a' undefined []
    
    // 如果解构不成功,变量的值就等于 undefined  
  1. 不完全解构栗子(等号左边的模式职匹配等号右边数组的一部分,但是依然可以解构成功)
    let [x,y] = [1,2,3];
    console.log(x,y); // 1 2
    
    let [a,[b],c] = [1,[2,3],4];
    console.log(a,b,c); // 1 2 4
  1. 不能解构栗子
    let [a] = 1;
    let [a] = false;
    let [a] = NaN;
    let [a] = undefined;
    let [a] = null;
    let [a] = {};

// 上面的语句都会报错 因为等号右边的值本身不具备Iterator接口或转为对象以后不具备Iterator接口

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

    let [x,y,z] = new Set('a','b','c');
    console.log(x); // a

2.默认值

看一段代码

    let [a = true] = [];
    console.log(a); // true
// 即使在等号右边找不到对应的值,也能输出默认值

    let [x, y = 'b'] = ['a']; // x = 'a' y = 'b'

再看一段代码

    let [x = 1] = [undefined];
    let [y = 1] = [null];
    console.log(x,y); // 1 null

这是为什么呢?因为ES6内部严格相等符号 (===) 判断一个位置是否有值,所以如果一个数组成员不严格等于undefined,默认值是不会生效的。因为null不严格等于undefined,所以默认值不会生效。

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

    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

对象的解构赋值

解构赋值不仅可以用于数组,还可以用于对象。

    let {foo,bar} = {foo:'aaa',bar:'bbb'};
    console.log(foo,bar); // 'aaa' 'bbb'

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

    let {baz} = {foo:'a',bar:'b'};
    conosle.log(baz); // undefined
    
    let {foo:baz} = {foo:'a',bar:'b'}
    console.log(baz); // a 
    
    let obj = {a:'a',b:'b'};
    let {a:aa,b:bb} = obj;
    console.log(aa,bb); // 'a' 'b'

实际上说明对象的结构赋值是下面形式的简写!

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

所以对象的解构赋值内部机制是找到同名属性,再赋值给对应的变量。其实真正被赋值的是后者。

字符串的解构赋值

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

    const [a,b,c,d,e] = 'hello';
    console.log(a,b,c,d,e); // h e l l o 

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

    let {length:len} = 'hello';
    console.log(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

圆括号问题

结构赋值虽然很方便,但是解析起来并不容易。对于编译器来说,一个式子到底是模式还是表达式,没有办法从一开始就知道,必须解析到(或者解析不到)等号才知道。
由此带来的问题是,如果模式中出现圆括号该怎么处理呢!ES6的规则是:只要有可能导致结构的歧义,就不得使用圆括号

用途

1.交换变量的值

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

上面的代码交换变量x和y的值,这样的写法非常清晰!!

2.从函数返回多个值

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

3.函数参数的定义

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

4.提取json数据

    let jsonData = {
        id: 42,
        status: 'OK',
        data: [1,2]
    }
    let {id,status,data:number} = jsonData;
    console.log(id,status,data:number); // 42 OK [1,2]

5.遍历 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

6.输入模块的制定方法

加载模块时,往往需要指定输入的方法,结构赋值使得输入语句非常清晰 ~

    const {aFun,bFun} = require('c');