JavaScript 解构赋值


prtyaa
prtyaa 2023-12-26 17:52:54 53505
分类专栏: 资讯
  • JavaScript 中最常用到的两种数据结构是 Object 和 Array。

    • 对象允许我们创建一个按键存储数据项的实体。
    • 数组允许我们将数据项收集到有序列表中。

    不过,当我们将这些数据传递给一个函数时,可能不需要作为一个整体的对象/数组传递,可能需要单独的部分。

    解构赋值是一种特殊的语法,它允许我们将数组或对象“解包”成一堆变量,因为有时这样更方便。

    解构也特别适用于含有大量参数、默认值等的复杂函数。很快我们就会看到。

    数组结构

    这个例子展示了一个数组是如何解构为变量的:

    // we have an array with the name and surname
    let arr = ["John", "Smith"]
    
    // destructuring assignment
    // sets firstName = arr[0]
    // and surname = arr[1]
    let [firstName, surname] = arr;
    
    alert(firstName); // John
    alert(surname);  // Smith
    

    现在可以就可以使用这些变量了,而不必访问数组的元素。

    配合 split 或者其他返回数组的函数使用特别方便:

    let [firstName, surname] = "John Smith".split(' ');
    alert(firstName); // John
    alert(surname);  // Smith
    

    如你所见,语法很简单。不过有几个特别的细节。让我们看更多的例子,以更好地理解它。

    解构不会破坏原数据结构

    之所以称之为“解构赋值”,是因为把数据项“解构”后复制到变量里。但是数组自身未改变。

    它只是如下写法的快捷方式:

    // let [firstName, surname] = arr;
    let firstName = arr[0];
    let surname = arr[1];
    

    使用逗号忽略元素

    数组里不想要的元素可以用一个多余的逗号忽略掉:

    // second element is not needed
    let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
    
    alert( title ); // Consul
    

    在上面的代码中,数组的第二个元素被跳过,第三个赋值给了 title,数组剩余的元素也跳过了(没有为它们分配变量)。

    可以应用于任意可迭代对象

    实际上,我们可以把解构应用于任意可迭代对象,而不仅限于数组:

    let [a, b, c] = "abc"; // ["a", "b", "c"]
    let [one, two, three] = new Set([1, 2, 3]);
    

    上面的代码可以运行,是因为解构赋值遍历了等号右侧的值。它在底层为等号右侧的值调用了 for...of 然后赋值。

    左侧赋值

    在左侧可以使用任何可赋值的变量。

    例如,可以是对象的属性:

    let user = {};
    [user.name, user.surname] = "John Smith".split(' ');
    
    alert(user.name); // John
    alert(user.surname); // Smith
    

    配合 .entries() 遍历

    我们可以用解构赋值配合它来实现一个对象键值对的遍历。

    let user = {
      name: "John",
      age: 30
    };
    
    // loop over keys-and-values
    for (let [key, value] of Object.entries(user)) {
      alert(`${key}:${value}`); // name:John, then age:30
    }
    

    对于 Map 的遍历代码类似,它更简单一些,因为它是可迭代的:

    let user = new Map();
    user.set("name", "John");
    user.set("age", "30");
    
    // Map iterates as [key, value] pairs, very convenient for destructuring
    for (let [key, value] of user) {
      alert(`${key}:${value}`); // name:John, then age:30
    }
    

    交换变量的技巧

    使用解构赋值交换两个变量的值有一个众所周知的技巧:

    let guest = "Jane";
    let admin = "Pete";
    
    // Let's swap the values: make guest=Pete, admin=Jane
    [guest, admin] = [admin, guest];
    
    alert(`${guest} ${admin}`); // Pete Jane (successfully swapped!)
    

    在这里,我们创建一个包含两个变量的临时数组,并交换它们顺序对其进行解构。

    我们可以用这种方式交换多个变量。

    剩余参数 '...'

    通常,如果数组比左边的变量列表长,那么“额外”的元素就会被忽略。

    例如,这里只有两个元素赋值,剩余的被忽略。

    let [name1, name2] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
    
    alert(name1); // Julius
    alert(name2); // Caesar
    // Further items aren't assigned anywhere
    

    如果我们也想收集剩下的元素,这时可以用三个点 "..." 多加一个参数。

    let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
    
    // rest is array of items, starting from the 3rd one
    alert(rest[0]); // Consul
    alert(rest[1]); // of the Roman Republic
    alert(rest.length); // 2
    

    rest 的值是由剩余元素组成的一个数组。

    我们可以用任意其他的变量名来替代 rest,只要它前面有三个点并且放在解构赋值的最后即可。

    let [name1, name2, ...titles] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
    // now titles = ["Consul", "of the Roman Republic"]
    

    默认值

    如果数组比左边的变量列表短,不会有报错。缺少的值被视为 undefined :

    let [firstName, surname] = [];
    
    alert(firstName); // undefined
    alert(surname); // undefined
    

    如果我们想要一个默认值来代替缺失的值,我们可以使用 =:

    // default values
    let [name = "Guest", surname = "Anonymous"] = ["Julius"];
    
    alert(name);    // Julius (from array)
    alert(surname); // Anonymous (default used)
    

    默认值可以是更复杂的表达式,甚至是函数调用。只有在未提供值的情况下,才会执行它们。

    例如,这里我们使用了 prompt 函数来指定两个默认值:

    // runs only prompt for surname
    let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"];
    
    alert(name);    // Julius (from array)
    alert(surname); // whatever prompt gets
    

    请注意:prompt 只会为缺失的值调用(surname)。

    对象解构

    解构赋值也可以用于对象。

    基本语法如下:

    let {var1, var2} = {var1:, var2:}
    

    右侧是一个已有的对象,我们希望将其拆分为变量。左侧格式类似于对象。在最简单的情况下,它是一个变量名列表放在 {} 中。

    例如:

    let options = {
      title: "Menu",
      width: 100,
      height: 200
    };
    
    let {title, width, height} = options;
    
    alert(title);  // Menu
    alert(width);  // 100
    alert(height); // 200
    

    options.title,options.width 和 options.height 属性被分配给相应的变量。

    变量名的顺序无关紧要,这样也可以:

    // changed the order in let {...}
    let {height, width, title} = { title: "Menu", height: 200, width: 100 }
    

    左侧的模式可能更复杂,它指定了属性和变量之间的映射关系。

    如果我们想将一个属性赋给另一个名字的变量,例如把 options.width 赋值到名为 w 的变量里,我们可以使用冒号来设置变量名:

    let options = {
      title: "Menu",
      width: 100,
      height: 200
    };
    
    // { sourceProperty: targetVariable }
    let {width: w, height: h, title} = options;
    
    // width -> w
    // height -> h
    // title -> title
    
    alert(title);  // Menu
    alert(w);      // 100
    alert(h);      // 200
    

    在上面的例子中 width 属性赋值到 w,height 属性赋值到 h,title 赋值到同名变量。

    对于可能缺失的属性值可以用 "=" 设置默认值,如下:

    let options = {
      title: "Menu"
    };
    
    let {width = 100, height = 200, title} = options;
    
    alert(title);  // Menu
    alert(width);  // 100
    alert(height); // 200
    

    和数组或函数参数类似,默认值可以是任意的表达式或者函数调用。它们会在值未提供时执行。

    下面的代码会调用 prompt 为 width 赋值,但对于 title 则不会。

    let options = {
      title: "Menu"
    };
    
    let {width = prompt("width?"), title = prompt("title?")} = options;
    
    alert(title);  // Menu
    alert(width);  // (whatever the result of prompt is)
    

    我们也可以同时使用冒号和等号:

    let options = {
      title: "Menu"
    };
    
    let {width: w = 100, height: h = 200, title} = options;
    
    alert(title);  // Menu
    alert(w);      // 100
    alert(h);      // 200
    

    如果一个复杂的对象有很多属性,我们可以只提取所需的属性:

    let options = {
      title: "Menu",
      width: 100,
      height: 200
    };
    
    // only extract title as a variable
    let { title } = options;
    
    alert(title); // Menu
    

    剩余模式 "..."

    如果对象的属性比我们的变量多会怎样?我们可以只接收部分然后把剩下的赋值到别处吗?

    我们可以用剩余模式,就像应用到数组那样。这个功能在一些旧版浏览器上不支持(例如 IE, 可以用 Babel 来兼容),但是在现代浏览器上支持情况良好。

    例如:

    let options = {
      title: "Menu",
      height: 200,
      width: 100
    };
    
    // title = property named title
    // rest = object with the rest of properties
    let {title, ...rest} = options;
    
    // now title="Menu", rest={height: 200, width: 100}
    alert(rest.height);  // 200
    alert(rest.width);   // 100
    

    如果不用 let 时的坑

    上面的例子中我们都是这样赋值的:let {...} = {...}。当然了,也可以不写 let 使用已有的变量名。不过有一个坑需要注意。

    这样不行:

    let title, width, height;
    
    // error in this line
    {title, width, height} = {title: "Menu", width: 200, height: 100};
    

    问题在于 JavaScript 会把 {...} 视为一个代码块。在 JavaScript 中代码块用于把语句进行分组,如下:

    {
      // a code block
      let message = "Hello";
      // ...
      alert( message );
    }
    

    所以在这里 JavaScript 会认为我们写了一个代码块,那就是为什么代码报错了。我们想要的是解构。

    要告知 JavaScript 它不是一个代码块,我们需要把这个表达式包裹在一对括号里(...):

    let title, width, height;
    
    // okay now
    ({title, width, height} = {title: "Menu", width: 200, height: 100});
    
    alert( title ); // Menu
    

    嵌套解构

    如果对象或者数组包含其他嵌套的对象和数组,我们可以在左侧使用更复杂的模式来抽取更深层的数据。

    下面的代码中 options 对象有一个 size 属性,它是另外一个对象;有一个 items 属性,它是一个数组。赋值左侧的模式和右侧的结构保持一致:

    let options = {
      size: {
        width: 100,
        height: 200
      },
      items: ["Cake", "Donut"],
      extra: true
    };
    
    // destructuring assignment split in multiple lines for clarity
    let {
      size: { // put size here
        width,
        height
      },
      items: [item1, item2], // assign items here
      title = "Menu" // not present in the object (default value is used)
    } = options;
    
    alert(title);  // Menu
    alert(width);  // 100
    alert(height); // 200
    alert(item1);  // Cake
    alert(item2);  // Donut
    

    options 对象的除了 extra 之外所有属性都赋值到左侧对应的变量里:

    最后,width, height, item1 和 item2 以及 title 都有值了。

    注意没有为 size 和 items 设置变量,因为我们提取了它们的内容。

    智能函数参数

    有时候一个函数有很多参数,大部分参数是可选的。对于用户界面尤其如此。设想有个函数用于创建菜单。它需要设置宽度,高度,标题,数据列表等等。

    这是一种不好的写法:

    function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
      // ...
    }
    

    在现实生活中,问题是怎么记住这些参数的顺序。一般来说 IDE 会提示,特别是代码注释规范时,不过... 另外一个问题是当大多数参数采用默认值即可时怎么调用函数。

    像这样?

    // undefined where default values are fine
    showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])
    

    那样很丑陋。而且随着参数的增多可读性很差。

    解构赋值这个救星来了!

    我们可以把参数作为一个对象,函数会把它们解构为变量:

    // we pass object to function
    let options = {
      title: "My menu",
      items: ["Item1", "Item2"]
    };
    
    // ...and it immediately expands it to variables
    function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
      // title, items – taken from options,
      // width, height – defaults used
      alert( `${title} ${width} ${height}` ); // My Menu 200 100
      alert( items ); // Item1, Item2
    }
    
    showMenu(options);
    

    也可以使用更复杂的结构,使用嵌套对象和冒号映射:

    let options = {
      title: "My menu",
      items: ["Item1", "Item2"]
    };
    
    function showMenu({
      title = "Untitled",
      width: w = 100,  // width goes to w
      height: h = 200, // height goes to h
      items: [item1, item2] // items first element goes to item1, second to item2
    }) {
      alert( `${title} ${w} ${h}` ); // My Menu 100 200
      alert( item1 ); // Item1
      alert( item2 ); // Item2
    }
    
    showMenu(options);
    

    完整的语法和解构赋值是一样的:

    function({
      incomingProperty: varName = defaultValue
      ...
    })
    

    对于参数组成的一个对象,会有一个名为 varName 的变量对应于 incomingProperty 属性,它的默认值是 defaultValue。

    注意这样的写法会假设 showMenu() 调用时传入了一个参数。如果想要所有的值都是默认值,需要传入一个空对象:

    showMenu({}); // ok, all values are default
    
    showMenu(); // this would give an error
    

    我们可以为这个参数对象指定一个默认值 {} 来解决这个问题:

    function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {
      alert( `${title} ${width} ${height}` );
    }
    
    showMenu(); // Menu 100 200
    

    上面的代码中,整个参数对象默认是 {},所以总是有值可以解构。

    总结

    • 解构赋值允许将一个对象或数组映射到许多变量上。
    • 对象解构赋值语法:

      let {prop : varName = default, ...rest} = object

      prop 属性值会赋值到 varName 变量里,如果没有这个属性,则使用 default 值。
      没有映射关系的属性会被拷贝到 rest 对象里。
    • 数组解构赋值语法:

      let [item1 = default, item2, ...rest] = array

      第一个元素赋值到 item1;第二个赋值到 item2,剩余的构成了 rest 数组。
    • 从嵌套数组/对象里抽取数据也是可以的,此时左侧数据结构需要和右侧的保持一致。

网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。

本文链接:https://www.xckfsq.com/news/show.html?id=30944
赞同 0
评论 0 条
prtyaaL2
粉丝 1 发表 2553 + 关注 私信
上周热门
如何使用 StarRocks 管理和优化数据湖中的数据?  2969
【软件正版化】软件正版化工作要点  2888
统信UOS试玩黑神话:悟空  2860
信刻光盘安全隔离与信息交换系统  2746
镜舟科技与中启乘数科技达成战略合作,共筑数据服务新生态  1280
grub引导程序无法找到指定设备和分区  1249
华为全联接大会2024丨软通动力分论坛精彩议程抢先看!  169
2024海洋能源产业融合发展论坛暨博览会同期活动-海洋能源与数字化智能化论坛成功举办  169
点击报名 | 京东2025校招进校行程预告  165
华为纯血鸿蒙正式版9月底见!但Mate 70的内情还得接着挖...  161
本周热议
我的信创开放社区兼职赚钱历程 40
今天你签到了吗? 27
信创开放社区邀请他人注册的具体步骤如下 15
如何玩转信创开放社区—从小白进阶到专家 15
方德桌面操作系统 14
我有15积分有什么用? 13
用抖音玩法闯信创开放社区——用平台宣传企业产品服务 13
如何让你先人一步获得悬赏问题信息?(创作者必看) 12
2024中国信创产业发展大会暨中国信息科技创新与应用博览会 9
中央国家机关政府采购中心:应当将CPU、操作系统符合安全可靠测评要求纳入采购需求 8

加入交流群

请使用微信扫一扫!