ES6系列入门学习记录:变量的解构赋值

前言

年都过去半个月了,我终于又从新开始更新了。虽然只是第二篇,可是我会继续加油努力,必定不会放弃更新的。在文章中如有什么不妥或者您有更多建议的话,欢迎和期待您给我留言,您的每个留言均可能成为我进步的助力,十分感谢。那就废话很少说直接开始吧。es6

概念

解构赋值,顾名思义,就是解开一个结构,而后从里面拿出值用来给变量赋值赋值。因此解构赋值主要是以数据类型来划分的。ajax

数组的解构赋值

var [a,b,c] = [1,2,3];
复制代码

上述代码算是最简单的数组解构赋值,其实也能够看作是数据的另外一种展现。好比上述代码与下面的代码实际上是同样的。json

var a=1,b=2,c=3;
复制代码

因此解构赋值最主要的做用,是可让咱们简化提取值的过程。数组

本质上,这种写法属于“模式匹配”,同模式下,左边的变量就会被赋予对应位置的右边的值。例如:bash

let [a,[[b],c]] = [1,[[2],3]];
a //1
b //2
c //3
复制代码

而且只要模式相同,即使部分位置的变量或者值为空,依旧能够进行匹配。数据结构

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

let [a,b,c] = [1,2];
a//1
b//2
c//undefind
复制代码

当解构不成功,即变量没有获得赋值,或者直接赋值undefind时,变量的值就会等于undefindasync

当匹配两边的模式相同,且长度不一样时,此时的解构赋值被称为不彻底解构。虽然叫不彻底解构,可是依旧算解构成功的。函数

let [a,b] = [1,2,3];
a //1
b //2
复制代码

上面一直提到一个前提状况,那就是模式相同,没错,这是比较须要注意的一点。当两边模式不一样时,解构赋值是会报错的。ui

let [a] = 1;
let [b] = false;
let [c] = {};
复制代码

在不严谨的状况下,咱们能够说,当两边的数据类型不一样时,解构赋值会出现报错。url

只要某种数据结构具备Iterator接口,均可以采用数组形式的解构赋值。Iterator接口最主要的功能是能够提供遍历命令for...of使用,不难猜想,其实数组解构赋值是一个将变量遍历循环,而后一一进行赋值的操做

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
复制代码

以上是阮一峰大神ES6入门里面的实例,因为我的目前还不清楚具体哪些数据结构具备Iterator接口,因此这里直接搬运一下。

默认值

let [a=1] = [];
a//1
复制代码

解构赋值操做时,能够设置一个默认值,若解构赋值操做室,对应位置上的值为undefind时,将会给变量赋值默认值。

须要注意的是,ES6内部使用严格相等运算符(===)来判断一个位置是否有值,我通常习惯称它为全等符号。因此,与通常的判断不一样,这里只有用于赋值的数组成员的值为undefined(严格等于undefined)时,默认值才会生效。

let [a=1] = [undefined];
a//1

let [b=1] = [null];
b//null

let [c=1] = [NaN];
c//NaN
复制代码

使用默认值时,还能够引用解构赋值的其余变量,但前提是该变量已声明。

let [a=1,b=a] = [];
a//1
b//1

let [a=b,b=1] = [];
//ReferenceError: b is not defined

var [a=b,b=1] = [];
a//undefined
b//1
复制代码

对象的解构赋值

let { a,b } = { a:'1',b:'2'};
a //'1'
b //'2'
复制代码

对象的解构赋值与数组的解构赋值最大的不一样之处,在于数组的解构赋值,变量的取值是由位置决定的;而对象的解构赋值,变量的取值是由属性名来决定的,只有变量与属性名相同,才能够取到值。

let { a , b } = { b : '2' , a : '1' };
a //1
b //2

let { a } = { b: '1' , c: '2' };
a//undefined
复制代码

当变量名与属性名不一致,却又须要进行解构赋值时,可使用变量再进行一次解构赋值。

let obj = { a : '1' , b : '2' };
let { a : c , b : d } = obj;
c //'1'
d //'2'
复制代码

而且,在这过程当中,实际被赋值的,实际上是cd。而ab是模式,起到相似于一个中介做用,不会被实际赋值。

let { a : b } = { a : '1'};
a //ReferenceError: a is not defined
b //'1'
复制代码

对象的解构赋值,与数组的解构赋值同样,也能够用于嵌套结构的对象。

let a = {
    b : [
        '1',
        { c : '2' }
    ]
};

let {
    b : [
        x ,
        { c }
    ]
} = a;

x // '1'
c // '2'
b // ReferenceError: b is not defined
复制代码

此时b只是模式,因此没法被赋值。

默认值

对象的解构赋值也有默认值,默认值的设置方式与数组相同,而不是依旧使用对象的内部写法。

let { a = 1} = {};
a // 1

let { b : 1 } = {};
//SyntaxError: Invalid destructuring assignment target
复制代码

默认值生效的条件与数组的解构赋值相同,属性值必须严格等于undefined才会生效。

let { a = 1 } = { a : undefined };
a //1 

let { b = 1 } = { b : null };
b // null

let { c = 1 } = { c : NaN };
c // NaN
复制代码

在对嵌套的对象使用解构赋值时,须要注意,若子对象所在的父属性不存在时,会报错。这也是我在工做中,发现比较常见的一种报错,仍是须要多多注意的。特别是在使用多层结构的时候,例如res.data.id

let { a: {b} }  = { c : '1' };
TypeError: Cannot destructure property `b` of 'undefined' or 'null'
复制代码

在使用对象解构赋值的时候,若是要对已经声明的变量进行解构赋值,须要当心。

let a;
{a} = {a:1};
//SyntaxError: Unexpected token =
复制代码

这里是由于JavaScript引擎会将{a}当作一个代码块,从而引起语法错误。因此须要避免将大括号写在行首

let a;
({a} = {a:1});
a // 1
复制代码

因为数组的本质是特殊的对象,所以能够对数组进行对象属性的解构赋值。

let a = [1, 2, 3];
let {0 : b, 2: c} = a;
b // 1
c // 3
复制代码

第二行代码的0和2表明的是数组的位置,能够简单理解为如下代码:

let a = [1,2,3];
let b =  a[0];
let c =  a[2];
复制代码

字符串的解构赋值

字符串也能够进行解构赋值,由于此时字符串被转换成了一个相似数组的对象。

let [a, b, c, d, e] = 'hello';
a // 'h'
b // 'e'
c // 'l'
d // 'l'
e // 'o'
复制代码

看到这里是否是感受这个过程有点眼熟,其实这个过程能够理解为如下代码:

let x = 'hello';
a = x[0];
b = x[1];
c = x[2];
...

复制代码

数值和布尔值的解构赋值

解构赋值时,若是等号右边是数值或者布尔值,则会先转化成对象。

let {toString:a} = 123;
a === Number.prototype.toString // true

let {toString:a} = 123;
a === Boolean.prototype.toString // true
复制代码

解构赋值的规则是,若等号右边的值不是对象或者数组,就会先将其转化成对象。因为undefinednull没法转化成对象,因此对其进行解构赋值时会报错。

let { a:b } = undefined;
//TypeError: Cannot destructure property `a` of 'undefined' or 'null'

let { a:b } = null;
//TypeError: Cannot destructure property `a` of 'undefined' or 'null'
复制代码

函数参数的解构赋值

function add({x,y]){
    return x+y;
}
add({1,2}); //3
复制代码

函数add的参数表面上为一个数组,可是在传入参数的那一刻,数组参数就被解构成了2个变量,xy

函数参数的解构也能够用默认值。

function move({x=0,y=0} = {} ) {
    return [x,y];
}
move({x:1}); // [1,0];
move({}); // [0,0];
复制代码

函数参数的解构有另外一种写法,会得出另外一种结果。

function move({x,y} = {x:0,y:0} ) {
    return [x,y];
}
move({x:1}); // [1,undefined];
move({}); // [undefined,undefined];
move(); // [0,0]
复制代码

上述的代码时为move函数参数设置默认值,而不是为解构后的xy设置默认值,因此会得出不同的结果。

圆括号

在使用解构赋值的时候,圆括号是否使用,是一个问题。

ES6的规则中说明,只要有可能致使解构歧义的,就不能使用圆括号。

但因为该规则的标准不容易衡量和辨别,因此通常是尽可能不使用圆括号。

不能使用圆括号的状况

  1. 变量声明语句

    let [(a)] = [1];
    let {x: (c)} = {};
    //上述两句代码显示为undefined
    
    let ({x: c}) = {};
    let {(x: c)} = {};
    let {(x): c} = {};
    let { o: ({ p: p }) } = { o: { p: 2 } };
    //上述四句代码会报错。
    复制代码

    上述代码发生这种状况,主要是由于它们都是变量声明语句,模式不能使用圆括号。

  2. 函数参数 函数参数也属于变量声明,所以不能带圆括号。

    function a( [ ( b ) ] ) { return c; }
    复制代码
  3. 赋值语句的模式

    ( { a:b } ) = { a:1 };
    
    [ ({a:b}) , { c:d } ] = [{},{}];
    复制代码

    不管是将整个模式放入圆括号中,仍是将部分模式放入圆括号中,都会致使报错。

解构赋值的用途

  1. 交换变量的值

    let a = 1;
    let b = 2;
    [a,b] = [b,a]
    复制代码
  2. 从函数返回多个值

    经过解构赋值,能够很方便的从数组或者对象里获取多个返回值。

    function arr(){
        return [1,2,3];
    }
    let [a,b,c] = arr();
    //返回一个数组
    
    
    function arr() {
        return {
            a:1,
            b:2
        };
    }
    let { a,b } = arr();
    复制代码
  3. 函数参数的定义 解构赋值能够方便地将一组参数与变量名对应起来。

    // 参数是一组有次序的值
    function f([a, b, c]) { ... }f([1, 2, 3]);
    
    // 参数是一组无次序的值
    function f({a, b, c}) { ... }f({z: 3, y: 2, x: 1});
    复制代码
  4. 提取JSON数据 在提取JSON对象中的数据时,解构赋值能起到很是简便和快速的做用,使得代码更加简洁。

    let jsonData = {
      id: 42,
      status: "OK",
      data: [867, 5309]};
    
    let { id, status, data: number } = jsonData;
    
    console.log(id, status, number);
    复制代码
  5. 函数参数的默认值 经过使用解构赋值,在给函数参数赋予默认值时,整个代码会显得更加简洁。

    jQuery.ajax = function (url, {
      async = true,
      beforeSend = function () {},
      cache = true,
      complete = function () {},
      crossDomain = false,
      global = true,
      // 在这里设置默认值
    } = {}) {
      // 这里则是赋值的内容,若为undefined,则使用默认值
    };
    复制代码
  6. 遍历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");
    复制代码

总结

在使用解构赋值的时候,总体感受上其实就是一个遍历过程的简化。我的感受最大的做用是能够将相似逻辑的代码进行过程简化,从而给代码瘦身。

同时在其中也发现了原文章中的部分细节错误。例如不能使用圆括号的状况中的第一点,示例代码中的前两行代码并无报错,而是显示undefined

而后这里给本身留一个小做业,是在和朋友聊上述细节错误时发现的一个问题:为何let [(a)] = [1];显示undefined,而用[(a)] = [1]则会显示[1]

参考文章

ECMAScript 6 入门:变量的解构赋值

相关文章
相关标签/搜索