❤ ES6-3【变量的解构赋值】

218 阅读6分钟

❤ ES6-3【变量的解构赋值】

1、[数组的解构赋值]

解构赋值语法

JavaScript的一种表达式,作用:方便从数组[]或者对象{} 快速拿值赋给定义的变量

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

语法:我把它称之为 一一对应

  • 以前
  let a = 1;
  let b = 2;
  let c = 3;
  • ES6
    let [a, b, c] = [1, 2, 3];

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

  • 下面是一些使用嵌套数组进行解构的例子

左边的格式和右边的一样即可:一一对应

    let [foo, [[bar], baz]] = [1, [[2], 3]];
    foo // 1
    bar // 2
    baz // 3let [ , , third] = ["foo", "bar", "baz"];
    third // "baz"let [x, , y] = [1, 2, 3];
    x // 1
    y // 3let [head, ...tail] = [1, 2, 3, 4];
    head // 1
    tail // [2, 3, 4]let [x, y, ...z] = ['a'];
    x // "a"
    y // undefined
    z // []
    
    
  • 解构不成功,变量的值就等于undefined
    let [foo] = [];
    let [bar, foo] = [1];
    ​
    foo的值都会等于undefined
  • 不完全解构,即等号左边的模式,只匹配一部分情况下,解构依然可以成功

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

如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错。

// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

上面的语句都会报错,因为等号右边的值,要么转为对象以后不具备 Iterator 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式)

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

本质上是只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值

function* fibs() {
      let a = 0;
      let b = 1;
      while (true) {
        yield a;
        [a, b] = [b, a + b];
      }
 
 }
let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5

代码解释:

这是一个斐波那契数列的生成器函数,用来生成斐波那契数列。

斐波那契数列:从0、1开始,后面每一项都是前面两项的和

fibs()生成()的斐波那契数列前几项如下:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

sixth对应的下标为5 的便是 5,相当于

let [first, second, third, fourth, fifth, sixth]=[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, .....无限长];

通过while循环,不断使用ES6引入的yield关键字,输出当前项,并将a和b更新为下一项和下下一项的值。这样就可以无限循环生成斐波那契数列。

yield:是ES6的新关键字使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的return关键字(类似return )

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

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

位置的判断和赋值ES6 内部使用的严格相等运算符(===),如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined

表达式也是惰性求值的,即只有在用到的时候,才会求值。

    function f() {
      console.log('aaa');
    }
    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 = y, y = 1] = [];     // ReferenceError: y is not defined `x`用`y`做默认值时,`y`还没有声明。
2、对象的解构赋值

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

    let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
    foo // "aaa"
    bar // "bbb"

对象的解构与数组有一个重要的不同。

数组的元素是按次序排列的,变量的取值由它的位置决定;

而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

我将这种行为概括为:同属

 let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
 foo // "aaa"
 bar // "bbb"
    
 let { baz } = { foo: 'aaa', bar: 'bbb' };
 baz // undefined

等号左边右边的两个同名属性的次序不一致,但是对取值完全没有影响。

第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined

默认值

对象的解构也可以指定默认值,规则等同数组默认值

    var {x = 3} = {};
    x // 3var {x, y = 5} = {x: 1};
    x // 1
    y // 5var {x: y = 3} = {};
    y // 3var {x: y = 3} = {x: 5};
    y // 5var { message: msg = 'Something went wrong' } = {};
    msg // "Something went wrong"

默认值生效的条件也是严格相等运算符的方式===

nullundefined不严格相等

3、字符串的解构赋值

字符串也可以解构赋值。此时,字符串被转换成了一个类似数组的对象。

const [a, b, c, d, e] = 'hello';
    a // "h"
    b // "e"
    c // "l"
    d // "l"
    e // "o"
4、数值和布尔值的解构赋值

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

    let {toString: s} = 123;
    s === Number.prototype.toString // truelet {toString: s} = true;
    s === Boolean.prototype.toString // true
5、函数参数的解构赋值

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

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

在传入参数的时候,数组参数就被解构成变量xy

可以拿来组合数据:

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

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

6、圆括号问题

解构赋值虽然很方便,但是解析起来并不容易。

对于编译器来说,必须解析到结果才能知道一个式子到底是模式,还是表达式,没办法从一开始就知道。

模式中出现圆括号处理起来相当麻烦。尽量就不要在模式中放置圆括号

7、用途

变量的解构赋值用途

(1)交换变量的值

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

(2)从函数返回多个值

    // 返回多个值 1
    function exam(){
      return [1,2,3]
    }
    let [a,b,c]=exam();
    console.log(a,b,c,'cc');
    // 1 2 3 'cc'
  // 返回一个对象function example() {
      return {
        name: '名字',
        age: 12
      };
    }

    let { name, age } = example();console.log(name,age); 
    //名字 定义的值必须上下name,age 相同 ,否则 undefined

(3)函数参数的定义

解构赋值可以方便地将一组参数与变量名对应起来。

    function f([x, y, z]) { ... }
    f([1, 2, 3]);
    
   function f({x, y, z}) { console.log(x,y,z,'zz') }
    f({z: 3, y: 2, x: 1});
    // 输出 1 2 3 'zz'  

(4)提取 JSON 数据

解构赋值对提取 JSON 对象中的数据,尤其有用。

 let jsonData = {
      id: 42,
      status: "OK",
      data: [867, 5309]
    };

let { id, status, data: number } = jsonData;

console.log(id, status, number);
// 输出 42, "OK", [867, 5309]

(5)函数参数的默认值


  jQuery.ajax = function (url, {
      async = true,
      beforeSend = function () {},
      cache = true,
      complete = function () {},
      crossDomain = false,
      global = true,
      // ... more config
    } = {}) {
    
    // ... do stuff
    };

指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || 'default foo';这样的语句。

(6)遍历 Map 结构

任何部署了 Iterator 接口的对象,都可以用for...of循环遍历。Map 结构原生支持 Iterator 接口,配合解构赋值,获取键名和键值就非常方便

 const 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
 
  

(7)输入模块的指定方法

加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。

 const { SourceMapConsumer, SourceNode } = require("source-map");