• ES6标准入门 第三章:变量的解构赋值


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

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


    1、数组的结解构赋值

    基本用法

    let [foo, [[bar], baz]] = [1,[[2], 3]];
    foo // 1
    bar // 2
    baz // 3
    let[ , , third] = [1,2,3];
    third // 3
    let[head, ...tail] = [1,2,3,4];
    head // 1
    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 // 1
    b // 2
    d // 4

    若等号右边为不可遍历的结构,将会报错;   解构赋值适用于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 // 5

    默认值

    解构赋值允许使用默认值

    [ 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 // 1
    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 // 1
    last  // 3

    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} = 123;
    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]);

    [[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 // 23
    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")
  • 相关阅读:
    bootstrap select动态赋值与赋默认值
    json学习
    JsonObject学习遇到的一个奇葩的问题,当value为空时,key不见了
    servlet+jsp实现文件上传,和图片预览
    解决webstorm运行vue项目时不能同步的问题
    webStrom配置less且自动生成.css和自动压缩为.min.css
    怎么升级本地vue版本
    css这一段时间学习中遇到的比较有用,但是容易忘记的属性
    vue.js将unix时间戳转换为自定义时间格式
    文档碎片——createDocumentFragment
  • 原文地址:https://www.cnblogs.com/james23dong/p/8520538.html
Copyright © 2020-2023  润新知