美文网首页react
初识解构赋值

初识解构赋值

作者: YeLqgd | 来源:发表于2017-10-24 21:25 被阅读0次

定义

解构赋值是什么?

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)

以前,为变量赋值,只能直接指定值。比如:

let a = 1
let b = 2
let c = 3

现在,ES6允许这样:

let [a, b, c] = [1, 2, 3]

或是这样:

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

上面三种方法的结果都是一样的。

本质上,后两种通过解构赋值的写法属于“模式匹配”,只要判定了等号两边的模式相同(解构),左边的变量就会被赋予对应的值(赋值)。ES6里大致有如下几种解构赋值的情况

数组的解构赋值

上面的第二个例子就是数组的解构赋值,这是刚好完全解构的情况。还有两种不完全解构的情况:

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

以及

let [a, b] = [1, 2, 3]
// 右边的3是多余的

let [ , , third] = ["foo", "bar", "baz"]
third // "baz"

从第一个例子可以看到当左边的c没有被解构成功时(即右边的数据结构没有与之对应的部分),那么这个变量的值为undefined,我们知道某个值为undefined的时候是有两种情况的,一是被声明了但是没有被赋值,二是显式的赋值为undefined;那究竟是哪种情况呢?

const [d]=[]
d // undefined

可以知道是第二种情况,没被解构成功的变量会被显式赋值为undefined

当数组内部有嵌套时,解构依然能正常进行:

let [foo, [[bar], baz]] = [1, [[2], 3]]
foo // 1
bar // 2
baz // 3
let [a, [b, c], d] = [1, [2, 3], 4]
a // 1
b // 2
c // 3
d // 4

除此之外还允许为解构赋值的变量设定默认值,即变量在解构后没有被赋到值(严格来说应该是被赋值为undefined)的情况下赋予该变量为某个默认值:

let [foo = true] = []
foo // true

let [x, y = 'b'] = ['a']
 // x='a', y='b'

let [x, y = 'b'] = ['a', undefined]
 // x='a', y='b'

let [x = 1] = [undefined]
x // 1

let [x = 1] = [null]
x // null

由以上几个例子可以知道,在变量有默认值时,只有当解构完之后赋的值为undefined时默认值才会生效,否则默认值会被解构之后的赋值所覆盖。

此外,当默认值为表达式时,表达式的求值是惰性的:

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

let [x = f()] = [1]
x //1,同时f()不会被执行

因为x被解构赋值为1,所以默认值不会生效,于是f()也就没有必要执行,它也确实没有被执行,因为如果执行的话会打印出aaa。当然,如果右边的[1]换成[]或者[undefined]的话,f()就会被执行。

默认值还可以引用解构赋值的其他变量,但前提是该变量必须已经声明,即被引用变量必须排在引用变量的左边:

let [x = 1, y = x] = [];     // x=1; y=1
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 = 1] = [];     // ReferenceError

上面最后一个表达式之所以会报错,是因为x用到默认值y时,y还没有声明。

对象的解构赋值

赋值的规则与数组的解构赋值本质上是一样的,都是解析两边的等号两边的模式,然后将对应位置上的数据关联起来。但与数组不同的时,对象是通过键来区分其内部成员,而非数组里的位置,所以对象的解构与变量的位置无关:

let {a, b} = {a: 1, b: 2}  
//a=1 b=2
let {b, a} = {a: 1, b: 2}
//a=1 b=2

我们知道(其实并不知道,好像是ES6才加的属性写法。。。。)

let a = 1
let obj = {a}
//obj ={a: 1}
let a = 1
let obj = {a: a} 
//obj ={a: 1}

所以let {a, b} = {a: 1, b: 2}其实是let {a: a, b: b} = {a: 1, b: 2}的简写。由于这里刚好左边的变量名与右边的键名一致,可以这样写,如果我们想换个其他的变量名就得把后面一部分写出来了:

let {a: c, b: d} = {a: 1, b: 2}
c // 1
d // 2
a // Uncaught ReferenceError: a is not defined
b // Uncaught ReferenceError: b is not defined

这里ab并非变量,而是用来匹配的模式,所以没有定义;如果同时想定义ab可以这样写let {a, b, a: c, b: d} = {a: 1, b: 2},即等价于let {a: a, b: b, a: c, b: d} = {a: 1, b: 2},这也是对象解构与数组解构不同的地方,数组解构是通过数组的下标来匹配,而一个下标只能容下一个变量,对象就不一样了,一个对象的键名可以容得下多个变量。

与数组一样,解构也可以用于嵌套结构的对象,直接看一个例子:

let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]
};

let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p//Uncaught ReferenceError: p is not defined,因为p只是匹配的模式

字符串的解构赋值

字符串也可以解构赋值,这是因为此时,字符串被转换成了一个类似数组的对象

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

const {length} = 'hello'
length //5

同时,作为三个基本包装类型之一,字符串还可以调用String对象的一些方法,于是:

let {indexOf} = 'hello'
indexOf === String.prototype.indexOf // true

可以知道,解构赋值时,如果等号右边是数值、字符串和布尔值三种基本包装类型时,则会将之先转为对象。同理,很容易可以推测另外两种基本包装类型的的解构赋值,不再赘述。

函数参数的解构赋值

参数的解构赋值本质数组的解构赋值或者对象的解构赋值,将一个数组或者对象作为参数传进一个函数,真正能被函数感知是参数是解构之后的被赋值的变量,比如在写React的无状态组件时我们可能会这样写:

ComponentA = (props) => {
  return (
    <div>props.prop1 + props.prop2</div>
  )
}

这里props是组件实例化时传入的一个包含所有prop的对象,那我们就可以这样写:

ComponentA = ({prop1, prop2}) => {
  return (
    <div>prop1 + prop2</div>
  )
}

这样就不必每次用到prop时都加个props.了,数组的情况也差不多。

再说下函数参数解构赋值有默认值的情况,先看一个例子:

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

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

这里{x = 0, y = 0} = {}的意思是赋参数xy在解构时的的默认值为0的同时设置函数move的默认参数为{},当什么都没传给函数move时,就等于把{}传给函数,此时xy的有一个解构的默认值都是0,当传入函数的值解构后可以对xy赋值(非undefined)时,则xy为新赋的值。

另一个例子:

function move({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

这里{x, y} = { x: 0, y: 0 }的意思是并不单独为xy赋默认值,只给函数的参数赋默认值为{ x: 0, y: 0},意即当什么都没传入的时候,等于将{ x: 0, y: 0}传给了move函数;所以当将{}作为参数传入的时候,{x, y}={}解构之后x, y的值都为undefined

总之,第一个例子是:对象解构赋值的默认值(x=0;y=0) + 函数参数的默认值({});第二个例子只有一个函数参数的默认值({x: 0, y: 0})。

用途

本质都是对象解构赋值或者数组解构赋值

其余详见
ECMAScript 6 入门-阮一峰

与之相关的知识点还有Iterator 和 for...of 循环Set 和 Map 数据结构

相关文章

网友评论

    本文标题:初识解构赋值

    本文链接:https://www.haomeiwen.com/subject/ugexpxtx.html