解构赋值语法是一种 Javascript 表达式。通过 解构赋值 可以将属性/值从对象/数组中取出,赋值给其他变量
引用链接
var a, b, rest; [a, b] = [10, 20]; console.log(a); // 10 console.log(b); // 20 [a, b, ...rest] = [10, 20, 30, 40, 50]; console.log(a); // 10 console.log(b); // 20 console.log(rest); // [30, 40, 50] ({ a, b } = { a: 10, b: 20 }); console.log(a); // 10 console.log(b); // 20 // Stage 4(已完成)提案中的特性 ({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}); console.log(a); // 10 console.log(b); // 20 console.log(rest); // {c: 30, d: 40}
对象和数组逐个对应表达式,或称对象字面量和数组字面量,提供了一种简单的定义一个特定的数据组的方法。
var x = [1, 2, 3, 4, 5];
解构赋值使用了相同的语法,不同的是在表达式左边定义了要从原变量中取出什么变量。
var x = [1, 2, 3, 4, 5]; var [y, z] = x; console.log(y); // 1 console.log(z); // 2
JavaScript 中,解构赋值的作用类似于 Perl 和 Python 语言中的相似特性。
var foo = ["one", "two", "three"]; var [one, two, three] = foo; console.log(one); // "one" console.log(two); // "two" console.log(three); // "three"
通过解构分离变量的声明,可以为一个变量赋值。
var a, b; [a, b] = [1, 2]; console.log(a); // 1 console.log(b); // 2
为了防止从数组中取出一个值为undefined的对象,可以在表达式左边的数组中为任意对象预设默认值。
var a, b; [a=5, b=7] = [1]; console.log(a); // 1 console.log(b); // 7
在一个解构表达式中可以交换两个变量的值。
没有解构赋值的情况下,交换两个变量需要一个临时变量(或者用低级语言中的XOR-swap 技巧)。
var a = 1; var b = 3; [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1
从一个函数返回一个数组是十分常见的情况。解构使得处理返回值为数组时更加方便。
在下面例子中,要让 [1, 2] 成为函数的 f() 的输出值,可以使用解构在一行内完成解析。
function f() { return [1, 2]; } var a, b; [a, b] = f(); console.log(a); // 1 console.log(b); // 2
你也可以忽略你不感兴趣的返回值:
function f() { return [1, 2, 3]; } var [a, , b] = f(); console.log(a); // 1 console.log(b); // 3 你也可以忽略全部返回值: [,,] = f();
当解构一个数组时,可以使用剩余模式,将数组剩余部分赋值给一个变量。
var [a, ...b] = [1, 2, 3]; console.log(a); // 1 console.log(b); // [2, 3]
注意:如果剩余元素右侧有逗号,会抛出 SyntaxError,因为剩余元素必须是数组的最后一个元素。
var [a, ...b,] = [1, 2, 3]; // SyntaxError: rest element may not have a trailing comma
用正则表达式的 exec() 方法匹配字符串会返回一个数组,该数组第一个值是完全匹配正则表达式的字符串,然后的值是匹配正则表达式括号内内容部分。解构赋值允许你轻易地提取出需要的部分,忽略完全匹配的字符串——如果不需要的话。
function parseProtocol(url) { var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url); if (!parsedURL) { return false; } console.log(parsedURL); // ["https://developer.mozilla.org/en-US/Web/JavaScript", "https", "developer.mozilla.org", "en-US/Web/JavaScript"] var [, protocol, fullhost, fullpath] = parsedURL; return protocol; } console.log(parseProtocol('https://developer.mozilla.org/en-US/Web/JavaScript')); // "https"
var o = {p: 42, q: true}; var {p, q} = o; console.log(p); // 42 console.log(q); // true
一个变量可以独立于其声明进行解构赋值。
var a, b; ({a, b} = {a: 1, b: 2});
备注:
赋值语句周围的圆括号 ( ... ) 在使用对象字面量无声明解构赋值时是必须的。
{a, b} = {a: 1, b: 2} 不是有效的独立语法,因为左边的 {a, b} 被认为是一个块而不是对象字面量。
然而,({a, b} = {a: 1, b: 2}) 是有效的,正如 var {a, b} = {a: 1, b: 2}
你的 ( ... ) 表达式之前需要有一个分号,否则它可能会被当成上一行中的函数执行。
可以从一个对象中提取变量并赋值给和对象属性名不同的新的变量名。
var o = {p: 42, q: true}; var {p: foo, q: bar} = o; console.log(foo); // 42 console.log(bar); // true
变量可以先赋予默认值。当要提取的对象对应属性解析为 undefined,变量就被赋予默认值。
var {a = 10, b = 5} = {a: 3}; console.log(a); // 3 console.log(b); // 5
一个属性可以同时 1)从一个对象解构,并分配给一个不同名称的变量 2)分配一个默认值,以防未解构的值是 undefined。
var {a:aa = 10, b:bb = 5} = {a: 3}; console.log(aa); // 3 console.log(bb); // 5
ES5 版本
function drawES5Chart(options) { options = options === undefined ? {} : options; var size = options.size === undefined ? 'big' : options.size; var cords = options.cords === undefined ? { x: 0, y: 0 } : options.cords; var radius = options.radius === undefined ? 25 : options.radius; console.log(size, cords, radius); // now finally do some chart drawing } drawES5Chart({ cords: { x: 18, y: 30 }, radius: 30 });
ES2015 版本
function drawES2015Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = {}) { console.log(size, cords, radius); // do some chart drawing } drawES2015Chart({ cords: { x: 18, y: 30 }, radius: 30 });
备注:
在上面的 drawES2015Chart 的函数签名中,解构的左手边被分配给右手边的空对象字面值:{size = 'big', cords = {x: 0, y: 0}, radius = 25} = {}。你也可以在没有右侧分配的情况下编写函数。但是,如果你忽略了右边的赋值,那么函数会在被调用的时候查找至少一个被提供的参数,而在当前的形式下,你可以直接调用 drawES2015Chart() 而不提供任何参数。如果你希望能够在不提供任何参数的情况下调用该函数,则当前的设计非常有用,而另一种方法在您确保将对象传递给函数时非常有用。
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: '/en-US/docs/Tools/Scratchpad' }; let { title: englishTitle, // rename translations: [ { title: localeTitle, // rename }, ], } = metadata; console.log(englishTitle); // "Scratchpad" console.log(localeTitle); // "JavaScript-Umgebung"
var 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 (var {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"
function userId({id}) { return id; } function whois({displayName: displayName, fullName: {firstName: name}}){ console.log(displayName + " is " + name); } var user = { id: 42, displayName: "jdoe", fullName: { firstName: "John", lastName: "Doe" } }; console.log("userId: " + userId(user)); // "userId: 42" whois(user); // "jdoe is John"
这段代码从 user 对象中提取并输出id、displayName 和 firstName。
计算属性名,如 object literals,可以被解构。
let key = "z"; let { [key]: foo } = { z: "bar" }; console.log(foo); // "bar"
Rest/Spread Properties for ECMAScript 提案(阶段 4)将 rest 语法添加到解构中。Rest 属性收集那些尚未被解构模式拾取的剩余可枚举属性键。
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40} a; // 10 b; // 20 rest; // { c: 30, d: 40 }
通过提供有效的替代标识符,解构可以与不是有效的 JavaScript标识符的属性名称一起使用。
const foo = { 'fizz-buzz': true }; const { 'fizz-buzz': fizzBuzz } = foo; console.log(fizzBuzz); // true
// 声明对象 和 自身 self 属性 var obj = {self: '123'}; // 在原型链中定义一个属性 prot obj.__proto__.prot = '456'; // test const {self, prot} = obj; // self "123" // prot "456"(访问到了原型链)