第三章 变量的解构赋值
这一章节阮一峰老师主要讲解 解构 的知识点,分别从 数组、对象、字符串、数值和布尔值、函数参数 等方向着手,讲解它们的使用方式。最后还谈了解构的问题与用途。
数组的解构赋值
书中从 基本用法、默认值 两个方面谈数组的解构。
基本用法
ES6 允许从数组和对象中按照一定模式获取值并赋值给变量,这一行为被称为解构赋值,其写法如下:
let [a, b, c] = [1, 2, 3]
console.log(a,b,c) // 1,2,3
本质上是遵循 “模式匹配” 原则,只要等式两边模式相等,就可以对应赋值。如:
let [a, [[b], c]] = [1, [[2], 3]]
console.log(a,b,c) // 1,2,3
若解构不成功或者对应位置没有值,则变量的值会等于 undefined
;若解构不完全(即等式右边的值比左侧多),则会正常解构赋值。示例代码如下:
// 解构不成功
let [a, b] = [1]
console.log(a,b) // 1,undefined
// 解构不完全
let [a, [[b], c]] = [1, [[2,3], 4]]
console.log(a,b,c) // 1,2,4
如果等式右侧是不允许遍历的解构,则会直接报错。
// 不可以解构,会报错
let [tmp] = 1
let [tmp] = false
let [tmp] = NaN
let [tmp] = undefined
let [tmp] = null
let [tmp] = {}
// Set 允许解构,本质是可以遍历的
let [tmp, foo] = new Set(['1', '2'])
console.log(tmp, foo) // '1', '2'
默认值
解构赋值允许设置默认值。ES6 内部严格使用相等运算符判断该位置是否有值,只有 === undefined
才会把默认值赋值给变量。示例代码如下:
let [a, b, c] = [1, , null]
console.log(a,b,c) // 1,undefined,null
若使用表达式作为默认值,则该表达式是惰性的,在需要使用时才会执行。
function f() {
return 1
}
let [x = f()] = [2]
// 上方代码可以等价替换为如下形式
let x
if ([2][0] === undefined) {
x = f()
} else {
x = [2][0]
}
默认值也可以设置为其他变量,前提是该变量必须已经声明,否则会报错。
let [x = 1, y = x] = [2] // x=2,y=2
let [x = 1, y = x] = [1, 2] // x=1,y=2
let [x = y, y = 2] = [2] // 2,2 这里没用到y,所以没报错
let [x = y, y = 2] = [] // 报错
对象的解构赋值
与数组一样,对象也可以使用解构。
数组解构需要讲究顺序一一对应,对象解构没有顺序要求,有名称要求,需要名称一一对应。
同样的,如果解构没有对应的值,则会赋值 undefined
。
let {a, b, c} = {a: 1, b: 3}
console.log(a,b,c) // 1,3,undefined
对象解构同样允许多层数组对象嵌套解构,示例代码如下:
let {a, b, b: [, {c}]} = {a: 1, b: [2, {c: 3}]}
console.log(a,b,c) // 1, [2,{c:3}], 3
上方代码中, b: [...]
的写法表示的是在 b
是匹配模式,冒号后面才是真正要赋值的变量。
对象解构也可以赋值默认值,与数组解构相同的,可以赋值变量,但是必须是已声明的变量。
let {a = 1, b = a} = {b: 3}
console.log(a,b) // 1,3
解构赋值可以给一个已存在的变量赋值。
let obj = {}
let {foo: obj.props} = {foo: {num:1, type: 2}}
console.log(obj) // {props: {num: 1, type: 2}}
如果要将已经声明的变量用于解构赋值,需要小心避免将大括号写在行首,以避免被 JavaScript 引擎误解为代码块而导致语法错误。正确的做法是将整个解构赋值语句放在圆括号里面。
let str
{str} = {str: 'daodao'} // SyntaxError: syntax error
// 正确写法
let str
({str} = {str: 'daodao'})
由于数组本质是特殊的对象 因此可以对数组进行对象属性的解构。
let arr = [l, 2 , 3]
let {0: first, [arr.length - l]: last} = arr; first // 1
last // 3
上面的代码对数组进行对象解构 数组 arr
键对应的值是 1, [arr.length - 1]
对应的值是 3。
字符串的解构赋值
字符串可以转换成类似数组的对象,因此可以对其解构。
const [a,b,c,d,e] = 'daodao'
console.log(a,b,c,d,e) // d,a,o,d,a
同样的,类似数组的对象也有 length
属性,因此也可以对该属性解构。
const {length: len} = 'daodao'
console.log(len) // 6
数值和布尔值的解构赋值
解构赋值时,如果等号右侧是数值型或布尔值型,会先转为对象。
let {toString: a} = 123
a === Number.prototype.toString // true
let {toString: a} = false
a === Boolean.prototype.toString // true
🔔 提示
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于 undefined
、 null
无法转为对象,所以对它们进行解构赋值时都会报错。
函数参数的解构赋值
对于函数的参数,可以使用解构赋值的方式来提取数组或对象中的值。通过解构赋值,可以更方便地获取传入参数的值并在函数体内使用。
[[1,2], [3,4]].map(([x, y]) => x + y) // [3, 7]
函数参数解构赋值中可以设置默认值。通过在解构赋值语法中设置默认值,可以确保在未传入参数或传入 undefined
时,变量能够取到默认值。
function fn({x = 0, y = 0} = {}) {
return [x, y]
}
fn({x: 3, y:8}) // [3, 8]
fn({x: 3}) // [3, 0]
fn({}) // [0, 0]
fn() // [0, 0]
若修改写法,则是给整个函数设置默认值,结果也会不一样。
function fn({x, y} = {x = 0, y = 0}) {
return [x, y]
}
fn({x: 3, y:8}) // [3, 8]
fn({x: 3}) // [3, undefined]
fn({}) // [undefined, undefined]
fn() // [0, 0]
当传入参数中出现 undefined
时触发函数参数默认值。
[1, , 3].map((x = 'undefined') => x) // [1, 'undefined', 3]
圆括号问题
圆括号有可能会导致解构赋值产生歧义,因此 ES6 作了规定:赋值语句允许添加括号,声明语句不允许添加括号。
// 赋值语句
[(b)] = {3}
({p: (d)} = {})
[(obj.prop)] = [3]
// 声明语句
let [(a)]= [1];
let {x: (c)} = {} ;
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};
let {o: ({p: p })} = { o: { p: 2 J }};
用途
解构赋值在现实生产环境中有很多实用的用途,阮一峰老师在书中罗列了几点:
交换值:在之前交换值需要使用到中间变量,其实通过解构赋值也能做到。
函数返回多个值:函数
return
只能返回一个值,若有多个值需要通过数组或对象的形式返回。解构可以获取到需要的值。函数参数定义:解构赋值可以方便地将一组参数与变量名对应起来。
提取
JSON
数据:可以通过解构快速提取JSON
格式的数据。函数参数默认值:函数参数可以通过解构设置默认值,不再需要额外赋值操作。
遍历
Map
结构:Map
可以使用for...of...
循环遍历,配合解构获取键名和键值就很方便。模块指定方法:加载模块时,往往需要指定输入的方法。解构赋值使得输入语句非常清晰
let x = 1
let y = 2
[x, y] = [y, x]
function fn1() {
return [1,2,3,4]
}
const [a, , c] = fn1()
function fn2() {
return {
bar: 1,
foo: 2
}
}
const {bar} = fn2()
// 有序
function fn([x, y, z]) {
// ...
}
fn([1, 2, 3])
// 无序
function fn({x, y, z}) {
// ...
}
fn({x: 4, y: 5, z: 6})
let data = {
id: 1,
name: 'dd',
num: [113, 24]
}
let {id, name, num} = data
function fn({
url: 'xxxxxxx',
method: 'post'
}) {}
let map = new Map([
['first', '1'],
['second', '2'],
])
for(let [key, value] of map) {
console.log('key:' + key, 'value:' + value)
}
const { SourceMapConsumer, SourceNode } = require('source-map');
总结
阮一峰老师在这一章节主要讲解了解构赋值的知识点,包括对数组、对象、字符串、数值和布尔值、函数参数等进行解构赋值的使用方式,并说明了解构赋值的问题和用途。
- 在数组的解构赋值中,可以利用模式匹配原则对数组进行赋值,同时也可以设置默认值,并且可以给已存在的变量赋值。
- 对象的解构赋值不需要考虑顺序,只需保证名称对应即可,同样可以设置默认值,并可以将已存在的变量用于解构赋值。
- 字符串也可以通过解构赋值的方式进行赋值。
- 数值和布尔值在解构赋值时会先转为对象。
- 函数参数的解构赋值可以方便地获取传入参数的值,并可以设置默认值。
- 圆括号有可能会导致解构赋值产生歧义,在赋值语句中可以添加括号,但在声明语句中不允许添加括号。
此外,解构赋值在实际生产环境中有很多实用的用途,包括交换值、函数返回多个值、函数参数定义、提取 JSON
数据、函数参数默认值、遍历 Map
结构、以及模块指定方法等。