美文网首页
变量的解构赋值

变量的解构赋值

作者: 阿go | 来源:发表于2018-02-04 21:20 被阅读0次

    ES6允许按照一定的模式从数组或者对象中提取值,然后对变量进行赋值,这种方式成为解构赋值,这使得在平时的编程中变得高效简洁,解构赋值有以下内容:

    数组的解构赋值

    1、基本用法

    //es6以前
    var a = 1;
    var b = 2;
    var c = 3;
    
    // es6
    let [a,b,c] = [1,2,3]
    //可得到
    a // 1
    b // 2
    c // 3
    

    由以上的赋值是从数组中提取值,然后【按照对应位置对变量进行赋值】
    其本质上属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
    如下例子:

    let [foo,[[bar],baz]] = [1,[[2],3]];
    foo // 1
    bar // 2
    baz //3
    //即以上的的等号两边的模式相同,可以进行对应位置变量的赋值
    
    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] = [1]
    x // 1
    y // undefined
    z // undefined
    // 这种情况属于解构不成功,所以未解构到的变量会等于undfined
    
    let [x,y] = [1,2,3]
    x // 1
    y // 2
    //这种情况属于不完全解构,即等号左边的模式只匹配一部分的等号右边的数组,但这种情况依然是解构成功
    
    但是,如果等号的右边不是数组(严格来说不是可遍历的结构),那么将会报错
    let [a] = 1;
    let [b] = false;
    let [c] = NaN;
    let [d] = undefined;
    let [e] = null;
    let [f] = {};
    //以上语句都会报错,因为等号右边的值或者是转为对象以后不具备Iterator接口(前5个表达式)
    //或者本身不具备Iterator接口(最后一个表达式)
    

    2、默认值

    解构赋值允许制定默认值,即es6内部会严格使用相等运算符(===)判断一个位置是否有真值,所以如果一个数组成员不严格等于undefined,默认值是不会生效的,反之,默认值生效。

    let [x = 1] = [undefined];
    x // 1
    
    let [ y = 2] = [];
    y // 2
    
    let [z = 3] = [null];
    z // null
    // 此时因为null不严格等于undefined
    
    // 如果默认值是一个表达式,那么这个表达式是惰性求值,即只有在用到时才会求值
    function f() {
        console.log(111);
    }
    let [x = f()] = [1];//此时,因为x能够取到值1,所以函数f根部不会执行
    
    
    以上的解构赋值代码等价于于:
    let x;
    if([1][0] === undefined) {
        x = f();
    }else{
        x = [1][0];
    }
    
    //默认值也可以引用解构赋值的其他变量,但必须保证在引用前,该变量以声明
    let [x = 1,y = 2] = []; // x=1,y=2
    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还没有声明
    

    2、对象的解构赋值

    对象的解构赋值与数组有一个重要的区别,即数组的元素时按次序排列的,变量的取值是由它的位置决定的;而对象的属性没有次序,变量必须与属性同名才能取到正确的值。
    对象的解构赋值的内部机制是先找到同名属性,然后再复制给对应的变量,真正被赋值的是后者而不是前者,见如下栗子:

    let {bar,foo} = {foo:1,bar:2};
    foo // 1
    bar // 2
    
    //以上赋值等价于下面代码,即当变量名与属性名相同时,可简写
    let {bar:bar,foo:foo} = {foo:1,bar:2};
    foo //1
    bar // 2
    
    //如果变量名与属性名不一样时,需要写全:
    let {foo:baz} = {foo:3,bar: 4};
    baz // 3
    foo // error: foo is not defined
    // 上面代码中,foo时匹配的模式,baz才是变量,真正被复制的是变量baz,而不是模式foo
    
    //解构同样也可以用于嵌套结构的对象
    let = {
        p: [
            'hello',
            {y: 'world'}
        ]
    };
    let {p:[x,{y}]} = obj;
    x // hello
    y // world
    //在此处,p是模式不是变量,因此不会被赋值
    
    对象的解构也可以指定默认值

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

    let {x = 3} = {};
    x // 3
    
    let {x,y=5} = {x :1};
    x // 1
    y // 5
    
    let {x:y = 5} = {};
    y //5
    
    let {x:y=5} = {x:3};
    y // 3
    
    let {x = 3} = {x:null};
    x // null
    
    注意:如果要将一个已经声明的变量用于解构赋值,必须非常小心
    let x;
    {x} = {x:1}; //SyntaxError: syntax error
    //以上的代码报错,是因为javascript引擎会将{x}理解成一个代码块,从而发生了语法错误
    //只有不将大括号写在行首,避免javascript将其解释为代码块,才能解决这个问题
    
    //正确写法
    let x;
    ({x}) = {x:1};
    x // 1
    

    3、字符串的解构赋值

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

    let [a,b,c] = 'god';
    a // g
    b // o
    c // d
    //类数组的对象都有一个属性length,因此还可以对这个属性进行解构赋值
    let {length:len} = 'hello';
    len // 5
    

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

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

    let {toString:s} = 123;
    s === Number.prototype.toString   // true
    //数值的包装对象都有toString属性,因此变量s都能取到值。
    

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

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

    5、函数参数的解构赋值

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

    // 函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成x和y;
    //所以对于函数内部的代码来说,它们能感受到的参数就是x和y。
    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]
    
    注意,一下写法则会得到不一样的结果
    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指定默认值,所以会的得到不一样的结果。
    

    6、圆括号问题

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

    不能使用圆括号的情况

    1、变量声明语句

    // 一下全部报错
    let [(a)] = [1];
    let {x:(c)} = {};
    let {(x:c)} = {};
    let ({x:c}) = {};
    let {(x):c} = {};
    let {o: ({p:p})} = {o:{p:2}};
    //上面6个语句都会报错,因为他们都是变量声明语句,模式不能使用圆括号。
    

    2、函数参数
    函数参数也属于变量声明,因此不能使用圆括号

    function f([(a)]) {return a}
    function f([z,(x)]) {return x}
    // 以上都会报错
    

    3、赋值语句的模式

    ({p:a}) = {p:1}
    ([a]) = [5];
    // 以上都会报错
    
    可以使用圆括号的情况

    可以使用圆括号的情况只有一种:赋值语句的飞魔士部分可以使用圆括号

    // 以下使用正确
    [(b)] = [3];
    ({p:(d)} = {p:3});
    //以上语句都可以正确执行,因为都是赋值语句而不是声明语句。
    

    7、变量解构赋值的用途

    主要可以适用在以下几种情况:
    1、交换变量

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

    2、从函数返回多个值

    function f(){
         return [1,2,3];
    }
    let [a,b,c] = f();
    

    3、函数参数的定义
    4.提取JSON数据
    5、函数参数的默认值
    6、遍历Map解构
    7、输入模块的制定方法

    const {a,b} = require('xxx.js')
    

    相关文章

      网友评论

          本文标题:变量的解构赋值

          本文链接:https://www.haomeiwen.com/subject/zehmzxtx.html