解构赋值
解构赋值做了什么?
- 语义: 把数组或对象"拆包"到一组变量;
- 非破坏性: 只是把元素复制进变量, 原数组/原对象不会被修改;
- 数组版: 按位置取值,
let [firstName, surname] = arr等价于分两行逐个下标赋值; - 对象版: 按属性名取值, 与左侧书写顺序无关;
- 右侧要求: 数组解构接受任何可迭代对象, 内部就是对着右侧跑一趟
for...of; - 左侧要求: 任何"可赋值目标"都行, 包括已有变量与对象属性;
let [firstName, surname] = "John Smith".split(" ");
let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two] = new Set([1, 2]);
const user = {};
[user.name, user.surname] = "John Smith".split(" ");
- 常见来源:
split()、Object.entries()、Map迭代器等返回数组的接口;
数组解构有哪些细节?
- 忽略元素: 用多余逗号跳过,
let [firstName, , title] = arr只取第 1 与第 3 项; - 取值不足: 数组比左侧短时不报错, 缺失项为
undefined; - 默认值: 写成
= value, 仅在对应位置为undefined时生效; - 惰性求值: 默认值可以是任意表达式或函数调用, 只在需要时才求值;
let [name = "Guest", surname = "Anonymous"] = ["Julius"];
// name = "Julius", surname = "Anonymous"
let [name2 = prompt("name?"), surname2 = prompt("surname?")] = ["Julius"];
// 只会弹出 surname 的输入框
- 交换变量: 临时造一个数组再换序解构,
[guest, admin] = [admin, guest], 参数更多也适用; - 剩余元素:
...rest收集剩余项为新数组, 且必须放在最后;
let [name1, name2, ...titles] = ["Julius", "Caesar", "Consul", "Imperator"];
// titles = ["Consul", "Imperator"]
对象解构如何重命名与取默认值?
- 基本:
let {title, width} = options把同名属性绑到同名变量; - 重命名:
{sourceProperty: targetVariable}, 冒号表示"哪个属性给哪个变量",{width: w}把options.width绑到w; - 默认值: 同一位置可写
{width: w = 100}, 重命名与默认值可组合; - 触发条件: 属性缺失或值为
undefined时用默认值, 同样支持复杂表达式; - 剩余属性:
...rest收集所有未被映射的属性, 结果为新对象; - 完整形态:
let {prop: varName = defaultValue, ...rest} = object; - 只取所需: 对象属性多时只列需要的键, 其余忽略;
let options = { title: "Menu", width: 100 };
let { width: w = 100, height: h = 200, ...rest } = options;
// w = 100, h = 200, rest = { title: "Menu" }
嵌套解构怎么写?
- 规则: 左侧模式与右侧结构同形即可, 对象套数组、数组套对象都支持;
- 结构层不产变量:
size: {width}只得到width, 没有size这个变量; - 缺失兜底: 嵌套位置也可写默认值;
let options = {
size: { width: 100, height: 200 },
items: ["Cake", "Donut"],
};
let {
size: { width, height },
items: [item1, item2],
title = "Menu", // 对象里没有, 走默认值
} = options;
// width=100, height=200, item1="Cake", item2="Donut", title="Menu"
函数参数解构解决了什么问题?
- 痛点: 参数多且大多可选时, 调用方必须记住顺序, 还要用
undefined占位; - 写法: 形参位置直接写解构模式, 实参传一个配置对象, 顺序无关且自解释;
- 默认值分工: 模式内默认值处理"缺属性", 参数默认
= {}处理"完全不传参"; - 注意: 不写
= {}时showMenu()会报错, 因为没有对象可以解构;
function showMenu({ title = "Untitled", width = 200, items: [i1, i2] } = {}) {
return `${title} ${width} ${i1}/${i2}`;
}
showMenu(); // 全部走默认值
showMenu({}); // 同上
循环里如何配合解构?
Object.entries: 返回[key, value]数组, 可直接解构遍历键值;Map迭代: 迭代项本身就是[key, value], 解构更直接;
for (const [key, value] of Object.entries({ name: "John", age: 30 })) {
console.log(`${key}:${value}`);
}
const map = new Map([["name", "John"]]);
for (const [key, value] of map) console.log(`${key}:${value}`);
- 范围: 对象解构只取对象自身的可枚举属性;
不用 let 解构为什么会报错?
- 原因: 主代码流中语句以
{开头会被当作代码块, 而不是解构模式; - 修法: 用括号把整个赋值包成表达式;
let title, width;
{title, width} = {title: "Menu", width: 200}; // 报错: 被当成代码块
({title, width} = {title: "Menu", width: 200}); // 正确