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

栏目: JavaScript · 发布时间: 5年前

内容简介:年都过去半个月了,我终于又重新开始更新了。虽然只是第二篇,但是我会继续加油努力,一定不会放弃更新的。在文章中若有什么不妥或者您有更多建议的话,欢迎和期待您给我留言,您的每一个留言都可能成为我进步的助力,十分感谢。那就废话不多说直接开始吧。解构赋值,顾名思义,就是解开一个结构,然后从里面拿出值用来给变量赋值赋值。所以解构赋值主要是以数据类型来划分的。上述代码算是最简单的数组解构赋值,其实也可以看做是数据的另一种展示。比如上述代码与下面的代码其实是一样的。

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

概念

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

数组的解构赋值

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

上述代码算是最简单的数组解构赋值,其实也可以看做是数据的另一种展示。比如上述代码与下面的代码其实是一样的。

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

所以解构赋值最主要的作用,是可以让我们简化提取值的过程。

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

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 时,变量的值就会等于 undefind

当匹配两边的模式相同,且长度不同时,此时的解构赋值被称为不完全解构。虽然叫不完全解构,但是依旧算解构成功的。

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

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

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

在不严谨的情况下,我们可以说,当两边的数据类型不同时,解构赋值会出现报错。

只要某种数据结构具有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]


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

组合数学教程

组合数学教程

范林特 / 刘振宏、赵振江 / 机械工业出版社 / 2007-4 / 49.00元

本书介绍组合数学中的基础理论和实际应用,讲述的内容非常广泛,讨论的问题涵盖组合数学所涉及的绝大部分领域。本书不仅包含了通常组合数学教科书中的经典内容,而且收集了若干新的内容,如Lovász筛法、范德瓦尔登积和式猜想、结合区组设计、码和设计等。 本书阐述深入浅出,简明易懂,适合作为高等院校高年级本科生与低年级研究生的组合数学课程教材,也适合作为数学和其他学科的研究人员的参考书。一起来看看 《组合数学教程》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具