JS ES6解构赋值

128 min read

简介

变量的解构可以应用于:

  1. 数组
  2. 字符串
  3. 对象
  4. 函数的参数
  5. 数值和布尔值

nullundefined 不行,强行解构要报错

const [ a, b ] = [1, 2]

const { x, y } = { x: 1, y: 2 }

const [ a, b, c, d, e ] = 'hello'

const f = ([a, b]) => {
  console.log(a,b)
}

let {toString } = 123;
s === Number.prototype.toString // true

let {toString } = true;
s === Boolean.prototype.toString // true

let { prop: x } = undefined // 报错:TypeError
let { prop: y } = null      // 报错:TypeError

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象
数值和布尔值转换成对象都存在toString属性,因此变量s都能取到值。
由于undefinednull无法转为对象,所以对它们进行解构赋值,都会报错。

用法

对于以上五种类型,解构赋值的用法存在一定差异

数组:

  1. 本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
  2. 赋值是按次序排列的
  3. 可以嵌套赋值
  4. 如果左边的模式对应的右边没有值,那么赋值undefined
let [a, [[b], c]] = [1, [[2], 3]];
a // 1
b // 2
c // 3

let [a, ...arr] = [1, 2, 3, 4];
a // 1
arr // [2, 3, 4]


let [ , , c] = [1, 2, 3];
c   // 3



let [a, , c] = [1, 2, 3];
a // 1
b // undefined
c // 3


let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

如果用解构数组的方法解构 不带 Iterator 接口 的数据类型,就会报错。

// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

什么是Iterator 接口

遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

Iterator 的作用有三个:

  • 一·是为各种数据结构,提供一个统一的、简便的访问接口;
  • 二·是使得数据结构的成员能够按某种次序排列;
  • 三·是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of使用。

事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。

比如 Set 结构的数据

let [x, y, z] = new Set(['a', 'b', 'c'])
x // "a"

字符串 也有Iterator 接口, 所以字符串的结构方式也是采用数组形式的

const [a, b, c, d, e] = 'hello'
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

对象的解构

  1. 对象的赋值与数组的赋值有所不同。数组赋值是按顺序赋值, 对象赋值按对应属性名赋值,与顺序无关。
  2. 左边的变量如果在右边没有对应的属性,那么就赋值undefined
  3. 在赋值时可以重命名
  4. 可以嵌套赋值
let { b, a } = { a: "aaa", b: "bbb" }
a // "aaa"
b // "bbb"

let { a, c } = { a: "aaa", b: "bbb" }
c // undefined

let { a, b: x } = { a: "aaa", b: "bbb" }
x   // "bbb"
b   // Uncaught ReferenceError: b is not defined


let obj = {a: { b: 'hello' }, c: '33'}
let { a: { b }, c } = obj
a   //  Uncaught ReferenceError: a is not defined
b   // hello
c   //  33

let { a, a: { b }, c } = obj

对象赋值其实是一种简写

let { b, a } = { a: "aaa", b: "bbb" }
a // "aaa"
b // "bbb"

//  等价于

let {  a: a, b: b } = { a: "aaa", b: "bbb" }

根据这个我们可以在对象解构赋值时 重命名

let {  a: a1, b: b1 } = { a: "aaa", b: "bbb" }

字符串和数组 都带有length属性 可以用对象解构形式取出

let arr = [1, 2]
let { length } = arr
length  // 2

let str = 'abc'
let { length } = str
length  // 3

函数参数的解构赋值

函数的参数可以是对象或数组,那么参数就可以使用解构赋值

const f1 = ({ x, y }) => {
  return  x / y
}

const f2 = ([x, y]) => {
  return x + y
}

解构赋值的默认值

当解构赋值时 左边模式在右边找不到对应的值或 对应的位置是undefined时,就会赋值undefined

如果解构时预见到可能会出现这种情况是,可以给变量给予默认值。 只有在获取的值严格等于undefined时才会使用默认值, false, null 不会

let [a, b = 2] = [1, undefined]  // a = 1, b = 2
let [a, b = 2] = [1]   
// a = 1, b = 2

let [a, b, c = 'z'] = 'xy'  
// a =x, b = y, c = z

let { a, b = 2, c = 3 } = {a: 1, b: undefined }
// a = 1, b = 2, c = 3
let { a: x = 1 } = {a: undefined }


function f({x = 0, y = 0}) {
  return [x, y]
}

如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会执行。

function f() {
  console.log('aaa')
}

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

let [x = f()] = []  // 'aaa'

解构赋值的应用

  • 交换变量的值
let x = 1
let y = 2

[x, y] = [y, x]
  • 从函数返回多个值

函数的返回值本身只能有一个

但是如果返回数组或者对象,获取返回值的时候使用解构赋值 就可以拿到多个变量的值了

// 返回一个数组

function getArr() {
  return [1, 2, 3]
}
let [a, b, c] = getArr();

// 返回一个对象

function getObj() {
  return { a: 1, b: 2 }
}
let { a, b } = getObj();
  • 函数参数的定义 解构赋值可以方便地将一组参数与变量名对应起来。
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);

// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
  • 提取 JSON 数据
res = {
  "code" : "200",
  "msg" : "操作成功!",
  "data" : [{
    "key" : "GBK",
    "value" : "GBK"
    }, ]
}

const { code, msg, data } = res
  • 输入模块的指定方法 加载模块时, 有时候只想要这个模块内的部分功能,不想全部加载,就可以使用解构赋值
const { Icon, message } = require("antd")

import {Icon, message } from 'antd'