解构赋值
解构赋值语法是一种 Javascript 表达式。可以将数组中的值或对象的属性取出,赋值给其他变量。—— MDN
我的理解是:解构赋值是为了便捷处理 数组、对象、函数参数的拓展。(不去专门研究对原始数据类型的解构,不钻这个牛角尖)。
ES6 允许按照一定模式,从目标数据中提取值,对变量进行赋值,这被称为解构(Destructuring)。
解构赋值写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。如“匹配不到”变量,值就等于undefined。
绑定与赋值
对于对象和数组的解构,有两种解构模式:绑定模式
和赋值模式
,它们的语法略有不同。
绑定模式:
以声明关键字(var、let 或 const)开始,然后,每个单独的属性必须绑定到一个变量或进一步解构。
const obj = { a: 1, b: { c: 2 } };
const { a, b: { c: d }} = obj; // 这里绑定了两个变量:`a`和`d`
const obj = { a: 1, b: { c: 2 } };
const { a, b: { c: d }} = obj; // 这里绑定了两个变量:`a`和`d`
因为所有变量共享相同的声明,可以解构两次获得可修改的和只读的数据:使用 let(可修改),使用 const(只读)。
const obj = { a: 1, b: { c: 2 } };
const { a } = obj; // a 只读 不可重新赋值
let { b: { c: d }} = obj; // d 可重新赋值
const obj = { a: 1, b: { c: 2 } };
const { a } = obj; // a 只读 不可重新赋值
let { b: { c: d }} = obj; // d 可重新赋值
赋值模式:
不以关键字开头。每个解构属性都被赋值给一个赋值目标,这个赋值目标可以事先用 var 或 let 声明,也可以是另一个对象的属性,一般来说,可以是任何可以出现在赋值表达式左侧的东西。
const numbers = [];
const obj = { a: 1, b: 2 };
({ a: numbers[0], b: numbers[1] } = obj); // numbers:[1, 2] 赋值目标 事先申明
({a: x, b: y} = obj); // x: 1, y: 2 不用以关键字开头
const numbers = [];
const obj = { a: 1, b: 2 };
({ a: numbers[0], b: numbers[1] } = obj); // numbers:[1, 2] 赋值目标 事先申明
({a: x, b: y} = obj); // x: 1, y: 2 不用以关键字开头
当不使用关键开头时,在赋值语句周围必须添加括号 ( ),且表达式( )前面需要有一个分号;
。否则它可能用于执行前一行的函数。
{ a, b } = { a: 1, b: 2 }; // 不是有效的独立语法,因为左侧的 {a, b} 被视为块而不是对象字面量。
({ a, b } = { a: 1, b: 2 }); // 是有效的
const { a, b } = { a: 1, b: 2 }; // 也是有效的。
{ a, b } = { a: 1, b: 2 }; // 不是有效的独立语法,因为左侧的 {a, b} 被视为块而不是对象字面量。
({ a, b } = { a: 1, b: 2 }); // 是有效的
const { a, b } = { a: 1, b: 2 }; // 也是有效的。
指定默认值
解构赋值还允许我们为变量指定默认值,当匹配不到值时,变量会被赋予默认值。
let [a,b,c,d] = [1,2,3]; // 未指定默认值
// a: 1,b: 2,c: 3, d: undefined
let [a,b,c,d = 4] = [1,2,3]; // 指定默认值
// a: 1,b: 2,c: 3, d:4
let [a,b,c,d] = [1,2,3]; // 未指定默认值
// a: 1,b: 2,c: 3, d: undefined
let [a,b,c,d = 4] = [1,2,3]; // 指定默认值
// a: 1,b: 2,c: 3, d:4
拓展运算符
const { a, ...others } = { a: 1, b: 2, c: 3 };
// others:{ b: 2, c: 3 }
const { a, ...others } = { a: 1, b: 2, c: 3 };
// others:{ b: 2, c: 3 }
适用于其他语法中
- for...in 和 for...of 循环中的循环变量;
- 函数参数;
- catch 绑定变量。
解构各类数据的示例
解构数组
基本使用
const foo = ["one", "two", "three"];
const [red, yellow, green] = foo; // red:one yellow:two green:three
const foo = ["one", "two", "three"];
const [red, yellow, green] = foo; // red:one yellow:two green:three
解构比目标多
const foo = ["one", "two", "three"];
const [red, yellow, green, gray] = foo;
// red:one; yellow:two; green:three; gray:undefined
const foo = ["one", "two", "three"];
const [red, yellow, green, gray] = foo;
// red:one; yellow:two; green:three; gray:undefined
变量交换
let a = 1;
let b = 3;
[a, b] = [b, a];
// a: 3; b:1;
let a = 1;
let b = 3;
[a, b] = [b, a];
// a: 3; b:1;
函数返回值为数组的结构
function f() {
return [1, 2];
}
const [a, b] = f(); // a: 1; b:2;
function f() {
return [1, 2];
}
const [a, b] = f(); // a: 1; b:2;
忽略某些返回值
const [a, , b] = [1, 2, 3] // a: 1; b:3;
const [a, , b] = [1, 2, 3] // a: 1; b:3;
使用绑定模式作为剩余属性 去原型上面解构对象的属性、方法
const [a, b, ...{ pop, push }] = [1, 2];
console.log(a, b); // 1 2
console.log(pop, push); // [Function pop] [Function push]
const [a, b, ...{ pop, push }] = [1, 2];
console.log(a, b); // 1 2
console.log(pop, push); // [Function pop] [Function push]
从正则表达式匹配项中提取值
function parseProtocol(url) {
const parsedURL = /^(\w+):\/\/([^/]+)\/(.*)$/.exec(url);
if (!parsedURL) {
return false;
}
console.log(parsedURL);
// ["https://developer.mozilla.org/zh-CN/docs/Web/JavaScript",
// "https", "developer.mozilla.org", "zh-CN/docs/Web/JavaScript"]
const [, protocol, fullhost, fullpath] = parsedURL;
return protocol;
}
parseProtocol("https://developer.mozilla.org/zh-CN/docs/Web/JavaScript") // // "https"
function parseProtocol(url) {
const parsedURL = /^(\w+):\/\/([^/]+)\/(.*)$/.exec(url);
if (!parsedURL) {
return false;
}
console.log(parsedURL);
// ["https://developer.mozilla.org/zh-CN/docs/Web/JavaScript",
// "https", "developer.mozilla.org", "zh-CN/docs/Web/JavaScript"]
const [, protocol, fullhost, fullpath] = parsedURL;
return protocol;
}
parseProtocol("https://developer.mozilla.org/zh-CN/docs/Web/JavaScript") // // "https"
在任何可迭代对象上使用数组解构 数组解构调用右侧的迭代协议( Iterator )。因此,任何可迭代对象(不一定是数组)都可以解构。
const [a, b] = new Map([[1, 2], [3, 4]]);
// a: [1, 2]
// b: [3, 4]
const [a, b] = new Map([[1, 2], [3, 4]]);
// a: [1, 2]
// b: [3, 4]
对象的解构
对象解构几乎等同于属性访问。
基本赋值
const user = {
id: 42,
isVerified: true,
};
const { id, isVerified } = user;
console.log(id); // 42
console.log(isVerified); // true
const user = {
id: 42,
isVerified: true,
};
const { id, isVerified } = user;
console.log(id); // 42
console.log(isVerified); // true
赋值给新的变量名 先匹配属性名,再赋值给变量。
const o = { p: 42, q: true };
const { p: foo, q: bar } = o;
console.log(foo); // 42
console.log(bar); // true
const o = { p: 42, q: true };
const { p: foo, q: bar } = o;
console.log(foo); // 42
console.log(bar); // true
const { p: foo } = o 从对象 o 中获取名为 p 的属性,并将其赋值给名为 foo 的局部变量。
从作为函数参数传递的对象中提取属性
// 从对象中解构参数
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "Jane",
lastName: "Doe",
},
};
function userId({ id }) {
return id;
}
userId(user) // 42
// 从对象中解构参数,重新命名参数
function userDisplayName({ displayName: dname }) {
return dname; // 重新命名的参数 dname
}
console.log(userDisplayName(user)); // `jdoe`
// 从对象中解构参数
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "Jane",
lastName: "Doe",
},
};
function userId({ id }) {
return id;
}
userId(user) // 42
// 从对象中解构参数,重新命名参数
function userDisplayName({ displayName: dname }) {
return dname; // 重新命名的参数 dname
}
console.log(userDisplayName(user)); // `jdoe`
设置函数参数的默认值
使用解构的方式,设置默认参数。
// 设置函数参数的默认值以对象举例子,也是是数组。
function drawChart({ size = "big", coords = { x: 0, y: 0 }, radius = 25} = {}) {
console.log(size, coords, radius);
return [size,coords,radius]
}
drawChart({ coords: { x: 18, y: 30 },radius: 30}); // ['big', { x: 18, y: 30}, 30]
drawChart(); // ['big', { x: 0, y: 0 }, 25]
// 设置函数参数的默认值以对象举例子,也是是数组。
function drawChart({ size = "big", coords = { x: 0, y: 0 }, radius = 25} = {}) {
console.log(size, coords, radius);
return [size,coords,radius]
}
drawChart({ coords: { x: 18, y: 30 },radius: 30}); // ['big', { x: 18, y: 30}, 30]
drawChart(); // ['big', { x: 0, y: 0 }, 25]
通常简洁的写法,不使用解构的方式。
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5, 2));// 10
console.log(multiply(5));// 5
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5, 2));// 10
console.log(multiply(5));// 5
解构嵌套对象和数组
const metadata = {
title: "Scratchpad",
translations: [
{
locale: "de",
localization_tags: [],
last_edit: "2014-04-14T08:43:37",
url: "/de/docs/Tools/Scratchpad",
title: "JavaScript-Umgebung",
},
],
url: "/zh-CN/docs/Tools/Scratchpad",
};
let {
title: englishTitle, // rename
translations: [
{
title: localeTitle, // rename
},
],
} = metadata;
console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"
const metadata = {
title: "Scratchpad",
translations: [
{
locale: "de",
localization_tags: [],
last_edit: "2014-04-14T08:43:37",
url: "/de/docs/Tools/Scratchpad",
title: "JavaScript-Umgebung",
},
],
url: "/zh-CN/docs/Tools/Scratchpad",
};
let {
title: englishTitle, // rename
translations: [
{
title: localeTitle, // rename
},
],
} = metadata;
console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"
For of
迭代和解构
const people = [
{
name: "Mike Smith",
family: {
mother: "Jane Smith",
father: "Harry Smith",
sister: "Samantha Smith",
},
age: 35,
},
{
name: "Tom Jones",
family: {
mother: "Norah Jones",
father: "Richard Jones",
brother: "Howard Jones",
},
age: 25,
},
];
for (const {
name: n,
family: { father: f },
} of people) {
console.log(`Name: ${n}, Father: ${f}`);
}
// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"
const people = [
{
name: "Mike Smith",
family: {
mother: "Jane Smith",
father: "Harry Smith",
sister: "Samantha Smith",
},
age: 35,
},
{
name: "Tom Jones",
family: {
mother: "Norah Jones",
father: "Richard Jones",
brother: "Howard Jones",
},
age: 25,
},
];
for (const {
name: n,
family: { father: f },
} of people) {
console.log(`Name: ${n}, Father: ${f}`);
}
// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"
解构对象时查找原型链 当解构一个对象时,如果属性本身没有被访问,它将沿着原型链继续查找。
const obj = {
self: "123",
__proto__: {
prot: "456",
},
};
const { self, prot } = obj;
// self "123"
// prot "456" (Access to the prototype chain)
const obj = {
self: "123",
__proto__: {
prot: "456",
},
};
const { self, prot } = obj;
// self "123"
// prot "456" (Access to the prototype chain)
解构原始数据类型
如果尝试解构基本类型的值,该值将被包装到相应的包装器对象中,并且在包装器对象上访问该属性。
const { a, toFixed } = 1;
console.log(a, toFixed);
//a: undefined
//toFixed: ƒ toFixed() { [native code] }
const { a, toFixed } = 1;
console.log(a, toFixed);
//a: undefined
//toFixed: ƒ toFixed() { [native code] }
解构 null 或 undefined 会抛出 TypeError