ES6 解构赋值

解构赋值

先记住一点,无论是数组的解构赋值还是对象的解构赋值,根本是赋值操作;

  • 数组的解构赋值根据变量的位置对应赋值
  • 对象的解构赋值根据匹配的模式进行赋值
  • 解构赋值必须符合语法
1
2
let {a,b};//报错
let [a,b];//报错

解构可以避免在对象赋值时产生中间变量:

1 数组的解构赋值(解构赋值的时候,必须符合解构赋值的格式,数据必须是可迭代的结构)

数组的元素是按次序排列的,变量的取值由它的位置决定

1
2
3
let [a,b,c] = [1,2,3]
//等价于
let a = 1, b = 2 , c = 3 ;

如果左边变量多余右边值,多余的变量将被赋值为undefined

1
2
let [a,b,c] = [1,2]
console.log(c) ;//undefined

如果左边变量少于右边值,右边多余的值将会被忽略

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

“链式”赋值:其实核心还是 赋值运算符的运算过程: 返回 = 右边的运算数,从右向左进行运算

1
2
3
4
let arr,first,second ;
arr = [first,second] = [1,2,3,4];
//arr // [1,2,3,4]
//first 1 second 2

变量值的”优先级”: 右边undefined < 左边默认值 < 右边除了undefined之外的其他值

1
2
3
4
let [a = 1 ,b=2 ,c] = [undefined,2222,3]; //右侧undefined不会赋值给a,右侧2222会赋值给b
console.log(a);//1
console.log(b);//2222
console.log(c);//3

解构赋值的原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let [a = b,b=5,c] = [1];
console.log(a);//1
console.log(b);//5
console.log(c);//undefined
//等价于 let a = 1, b = 5 , c = undefined ;
//以下情况会报错
let [a = b,b=5,c] = [];//直接报错
console.log(a);//
console.log(b);//
console.log(c);//
//等价于 let a = b , b = 5 , c = undefined ;//let声明的变量使用之前必须先被声明
--------------------------------------------
// let a = b ;//b is not defined
// let b = 5 ;
// let c ;
//对于let声明变量,变量的使用必须在声明之前才可以使用

看下var声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var [a = b,b=5,c] = [];//这个不会报错,所以需要理解数据结构赋值的原理
console.log(a);//undefined
console.log(b);//5
console.log(c);//undefined
//--------------------------------------------------
//左边进行声明
// var a = b ;//这个相当于对 b 进行了变量提升,但是没有运行到b=5的时候,a的值被赋予undefined;
// var b = 5 ;
// var c ;
// console.log(a);//undefined
//然后右边进行赋值,原来有默认值的不会发生变化
// ------------------------------------------------------
// var a = b ;//b is not defined
//
// var c ;
// console.log(a);//
// ----------------------------------------------------------
1
2
3
4
5
6
7
//如果等号的右边不是数组或者严格地说,不是可遍历的结构,那么将会报错。如下
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

2 对象的解构赋值

对象的属性没有次序,变量必须与属性同名,才能取到正确的值

对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者

1
2
3
4
5
let {foo,bar} = {foo:"aaa",bar:"bbb"};
let {bar,foo} = {foo:"aaa",bar:"bbb"};
//foo aaa
//bar bbb
//这点和数组的结构赋值不同,数组的结构赋值是按照元素的位置进行解构赋值,而对象的解构赋值是根据无序的属性名进行匹配,然后在进行赋值

如果匹配不到对应的属性名,那么将会被赋值为undefined

1
2
let {baz} = {foo1:'aaa',foo2:'bbb'}
console.log(baz);//undefined
1
2
let {baz} = {}
console.log(baz);//undefined

对象的解构赋值,根本是给属性的属性值进行的赋值

1
2
let{foo} = {foo:'aaa',bar:'bbb'};
console.log(foo);//aaa
1
2
3
let{foo:baz} = {foo:'aaa',bar:'bbb'};//采用这种写法时,变量的声明和赋值是一体的
console.log(baz);//aaa
console.log(foo);//foo is not defined

从上面两段代码可以看出来,其实对象的解构赋值,

  • 根本还是对象的属性的简洁语法表示
  • 解构赋值是先进行变量的声明
1
2
let { foo,bar} = { foo: "aaa", bar: "bbb" };
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };

如果是采用以下赋值形式{ } 必须用( ) 括起来,否则浏览器会认为那是一个代码块,会报错

1
2
({foo:baz} = {foo:'aaa'});
console.log(baz);//aaa

为了说明对象的解构赋值的匹配模式的,看下面的代码

1
2
3
4
let{foo:baz} = {};
console.log(baz);//undefined
console.log(foo);//foo is not defined
//这个时候,我们应该能理解 let{foo:baz} 声明的变量是baz ,并不是foo,foo是匹配的模式,匹配的根据

体会下下面的两段代码

1
2
3
4
5
6
console.log(name);//报错 name is not defined
let {heheh:name} = {heheh:'aaa'};
console.log(name);
//这个也证明了 let {heheh:name} 声明的是 : 后面的变量
//而let{foo ,bar } 其实是分为两步,第一对象的简洁写法{foo,bar} ==> {foo:foo,bar:bar}
//所以let声明的变量还是 : 冒号后面的变量,然后在进行解构赋值操作
1
2
3
console.log(name);//aaa
({heheh:name} = {heheh:'aaa'});
console.log(name);//aaa

对象的解构赋值是可以嵌套的,同样对于匹配模式(属性名)还是要有深刻的认识

1
2
3
4
5
({p:[x,{y:z}]} = {p:['hello',{}]} );
console.log(x);//hello
console.log(z);//undefined
console.log(y);//y is not defined
// p 和 y 都是匹配模式

undefined再取属性的时候会报错

1
2
3
let _temp = {bar:"baz"};
console.log(_temp.foo);//undefined
console.log(_temp.foo.anything);// Cannot read property 'anything' of undefined

如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错。

1
2
let {foo:{bar:baz}} = {hehe:{bar:'value'}}
//上面代码中,等号左边对象的foo属性,对应一个子对象。该子对象的bar属性,解构时会报错。原因很简单,因为foo这时等于undefined,再取子属性就会报错,请看下面的代码