Skip to content
作者:Yuan Tang
更新于:5 个月前
字数统计:2.3k 字
阅读时长:8 分钟

第三章 变量的解构赋值

这一章节阮一峰老师主要讲解 解构 的知识点,分别从 数组、对象、字符串、数值和布尔值、函数参数 等方向着手,讲解它们的使用方式。最后还谈了解构的问题与用途。

数组的解构赋值

书中从 基本用法、默认值 两个方面谈数组的解构。

基本用法

ES6 允许从数组和对象中按照一定模式获取值并赋值给变量,这一行为被称为解构赋值,其写法如下:

js
const [a, b, c] = [1, 2, 3]
console.log(a, b, c) // 1,2,3

本质上是遵循 “模式匹配” 原则,只要等式两边模式相等,就可以对应赋值。如:

js
const [a, [[b], c]] = [1, [[2], 3]]
console.log(a, b, c) // 1,2,3

若解构不成功或者对应位置没有值,则变量的值会等于 undefined ;若解构不完全(即等式右边的值比左侧多),则会正常解构赋值。示例代码如下:

js
// 解构不成功
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

如果等式右侧是不允许遍历的解构,则会直接报错。

js
// 不可以解构,会报错
let [tmp] = 1
let [tmp] = false
let [tmp] = Number.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 才会把默认值赋值给变量。示例代码如下:

js
const [a, b, c] = [1, , null]
console.log(a, b, c) // 1,undefined,null

若使用表达式作为默认值,则该表达式是惰性的,在需要使用时才会执行。

js
function f() {
  return 1
}

let [x = f()] = [2]

// 上方代码可以等价替换为如下形式

let x
if ([2][0] === undefined)
  x = f()
else
  x = [2][0]

默认值也可以设置为其他变量,前提是该变量必须已经声明,否则会报错。

js
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

js
const { a, b, c } = { a: 1, b: 3 }
console.log(a, b, c) // 1,3,undefined

对象解构同样允许多层数组对象嵌套解构,示例代码如下:

js
const { a, b, b: [, { c }] } = { a: 1, b: [2, { c: 3 }] }
console.log(a, b, c) // 1, [2,{c:3}], 3

上方代码中, b: [...] 的写法表示的是在 b 是匹配模式,冒号后面才是真正要赋值的变量。

对象解构也可以赋值默认值,与数组解构相同的,可以赋值变量,但是必须是已声明的变量。

js
const { a = 1, b = a } = { b: 3 }
console.log(a, b) // 1,3

解构赋值可以给一个已存在的变量赋值。

js
let obj = {}

let {foo: obj.props} = {foo: {num:1, type: 2}}
console.log(obj) // {props: {num: 1, type: 2}}

如果要将已经声明的变量用于解构赋值,需要小心避免将大括号写在行首,以避免被 JavaScript 引擎误解为代码块而导致语法错误。正确的做法是将整个解构赋值语句放在圆括号里面。

js
let str
{str} = {str: 'tydumpling'} //  SyntaxError: syntax error 

// 正确写法
let str
({str} = {str: 'tydumpling'})

由于数组本质是特殊的对象 因此可以对数组进行对象属性的解构。

js
const arr = [l, 2, 3]
const { 0: first, [arr.length - l]: last } = arr; first // 1
last // 3

上面的代码对数组进行对象解构 数组 arr 键对应的值是 1, [arr.length - 1] 对应的值是 3。

字符串的解构赋值

字符串可以转换成类似数组的对象,因此可以对其解构。

js
const [a, b, c, d, e] = 'tydumpling'
console.log(a, b, c, d, e) // d,a,o,d,a

同样的,类似数组的对象也有 length 属性,因此也可以对该属性解构。

js
const { length: len } = 'tydumpling'
console.log(len) // 6

数值和布尔值的解构赋值

解构赋值时,如果等号右侧是数值型或布尔值型,会先转为对象。

js
let { toString: a } = 123
a === Number.prototype.toString // true

let { toString: a } = false
a === Boolean.prototype.toString // true

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于 undefinednull 无法转为对象,所以对它们进行解构赋值时都会报错。

函数参数的解构赋值

对于函数的参数,可以使用解构赋值的方式来提取数组或对象中的值。通过解构赋值,可以更方便地获取传入参数的值并在函数体内使用。

js
[[1, 2], [3, 4]].map(([x, y]) => x + y) // [3, 7]

函数参数解构赋值中可以设置默认值。通过在解构赋值语法中设置默认值,可以确保在未传入参数或传入 undefined 时,变量能够取到默认值。

js
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]

若修改写法,则是给整个函数设置默认值,结果也会不一样。

js
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 时触发函数参数默认值。

js
[1, , 3].map((x = 'undefined') => x) // [1, 'undefined', 3]

圆括号问题

圆括号有可能会导致解构赋值产生歧义,因此 ES6 作了规定:赋值语句允许添加括号,声明语句不允许添加括号。

js
// 赋值语句
[(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 } ;

用途

解构赋值在现实生产环境中有很多实用的用途,阮一峰老师在书中罗列了几点:

  1. 交换值

    在之前交换值需要使用到中间变量,其实通过解构赋值也能做到。

    js
    const x = 1
    const y = 2
      [x, y] = [y, x]
  2. 函数返回多个值

    函数 return 只能返回一个值,若有多个值需要通过数组或对象的形式返回。解构可以获取到需要的值。

    js
    function fn1() {
      return [1, 2, 3, 4]
    }
    const [a, , c] = fn1()
    
    function fn2() {
      return {
        bar: 1,
        foo: 2
      }
    }
    const { bar } = fn2()
  3. 函数参数定义

    解构赋值可以方便地将一组参数与变量名对应起来。

    js
    // 有序
    function fn([x, y, z]) {
      // ...
    }
    fn([1, 2, 3])
    
    // 无序
    function fn({ x, y, z }) {
      // ...
    }
    fn({ x: 4, y: 5, z: 6 })
  4. 提取 JSON 数据

    可以通过解构快速提取 JSON 格式的数据。

    js
    const data = {
      id: 1,
      name: 'dd',
      num: [113, 24]
    }
    
    const { id, name, num } = data
  5. 函数参数默认值

    函数参数可以通过解构设置默认值,不再需要额外赋值操作。

    js
    function fn({
      url: 'xxxxxxx',
      method: 'post'
    }) {}
  6. 遍历 Map 结构

    Map 可以使用 for...of... 循环遍历,配合解构获取键名和键值就很方便。

    js
    const map = new Map([
      ['first', '1'],
      ['second', '2'],
    ])
    
    for (const [key, value] of map)
      console.log(`key:${key}`, `value:${value}`)
  7. 模块指定方法

    加载模块时,往往需要指定输入的方法。解构赋值使得输入语句非常清晰

    js
    const { SourceMapConsumer, SourceNode } = require('source-map')

总结

阮一峰老师在这一章节主要讲解了解构赋值的知识点,包括对数组、对象、字符串、数值和布尔值、函数参数等进行解构赋值的使用方式,并说明了解构赋值的问题和用途。

  • 在数组的解构赋值中,可以利用模式匹配原则对数组进行赋值,同时也可以设置默认值,并且可以给已存在的变量赋值。
  • 对象的解构赋值不需要考虑顺序,只需保证名称对应即可,同样可以设置默认值,并可以将已存在的变量用于解构赋值。
  • 字符串也可以通过解构赋值的方式进行赋值。
  • 数值和布尔值在解构赋值时会先转为对象。
  • 函数参数的解构赋值可以方便地获取传入参数的值,并可以设置默认值。
  • 圆括号有可能会导致解构赋值产生歧义,在赋值语句中可以添加括号,但在声明语句中不允许添加括号。

此外,解构赋值在实际生产环境中有很多实用的用途,包括交换值、函数返回多个值、函数参数定义、提取 JSON 数据、函数参数默认值、遍历 Map 结构、以及模块指定方法等。

Contributors

Yuan Tang