解构赋值
什么是解构赋值
顾名思义,解构赋值就是将集合中的值提取出来,再对变量进行赋值,其实就是返回的一个遍历器对象(Iterator),如果赋值右边不是可遍历的结构就会报错
- 数组的解构
let foo = 1;
let bar = 2;
let test = 3;
// 以前的赋值方式
let [foo, bar, test] = [1, 2, 3]
// 用了数组结构之后
数组的解构赋值,会按照位置依次赋值
let [foo, bar, car] = ['foo'];
// bar和car会变为undefined, 因为解构失败
let [foo, bar, ...car] = ['foo'];
// bar还是undefined 但是此时car为一个空数组
let [foo, ...bar] = [1, 2, 3, 5, 6];
// foo只会占据一位,bar会把剩下的位置全部占用并且赋值形成一个新数组
如果解构失败那么就会返回undefined
let [foo] = [];
let [bar, foo] = [1];
// 以上foo都会返回undefined
```
还有一种不完全解构
```javascript
let [foo, bar] = [1, 4, 9];
let [foo, [car, wife], bar] = [1, [5, 7, 9], 10];
// 上面依旧可以解构成功,没有赋值的值会留原数组中,以便后续使用
事实上,只要某种结构具有遍历器对象Iterator就可以采用数组的解构赋值
解构赋值可以采用default默认值的写法,它采用赋值右边完全等于undefined时才会触发
let [foo = 1] = [];
let [x, y = 'foo'] = ['bar'];
// x = 'bar' y = 'foo';
let [x, y = 'foo'] = ['bar', undefined];
// x = 'bar' y ='foo'; 由于y传了undefined所以遵循===规则使用默认值
默认值还可以使用已经声明的值
let [foo = 1, bar = foo] = [undefined, undefined];
// foo = 1, bar = 1; bar使用了foo的变量且传值是undefined,所以都是1
let [foo = 1, bar = foo] = [2];
// foo = 2, bar = 2; 由于传值不是undefined所以foo使用2,bar又等于foo
let [foo = 1, bar = foo] = [1, 2];
// foo = 1, bar = 2; 由于俩值都穿了值,所以就不存在使用默认值了,直接赋值
let [foo = bar, bar = 1] = [];
// error 因为bar还没有定义呢,你就是用就会报错
- 对象的解构
解构赋值,既可用于数组又可用于对象
let {bar, foo} = {bar: 'bar', foo: 'foo'};
// bar: 'bar', foo: 'foo'
数组采用索引来赋值,而对象没有这种机制,赋值变量必须与属性值相同才能赋值
let {foo, bar} = {bar: 'bar', foo: 'foo'}
// bar: 'bar'
// foo: 'foo'
let {baz} = {bar: 'foo', foo: 'bar'}
// baz: undefined
首先上述例子是对象解构,所以不会运用索引机制,他会先看属性名和变量名是否一致,并且是否拥有,紧接着把属性变量名相同的进行赋值
上述两个例子,虽然第一个变量顺序和属性不符,但是对象的机制就是观察是否相同,不会去管索引(对象中就没有索引),紧接着第二个因为没有相同属性值,所以变量baz赋值不成功,结果undefined!
结构失败,变量为undefined
let {name} = {sex: 'male'}
// name: undefined
等号右边属性中没有name,所以解构不成功,值为undefined
js内部的对象也可以解构
let {log, sin, cos} = Math;
let {log. dir} = console;
let {write} = document;
log('hello, es6');
// 对于js中现有对象都可以采用这种解构,来缩写代码的占用(说白了,有时候可以懒一下)
变量与属性不一致时,需采用以下写法
let {bar: baz} = {bar: 'bar', foo: 'foo'}
// baz: 'bar'
let obj = {first: 'hello', last: 'world'}
let {first: f, last: l} = obj;
// f: 'hello'
// l: 'world'
// 上述就等于
let {first: f, last: l} = {first: 'hello', last: 'world'}
// f: 'hello'
// l: 'world'
实际对象结构是这样的机制
let {foo: foo, bar: bar} = {foo: 'foo', bar: 'bar'}
// foo: 'foo'
// bar: 'bar'
也就是说,对象的解构赋值内部机制,是先找到同名属性,再赋给对应的变量,真正被赋值的是后者,而不是前者
下面来验证这个说法
let {bar: baz} = {bar: 'foo', foo: 'bar'}
// baz: 'foo'
// bar: error: bar is not defined
上述代码bar只是模式,而baz才是变量,被赋值的是baz
对象解构也可以进行嵌套
let obj = {
target: [
'son',
{
person: 'person'
}
]
}
let { target: [x, {y}] } = obb;
// x: 'son'
// y: 'person'
// target: error : target is not defined
此时的target只是模式
下面这种写法可以使target变为变量
let obj = {
target: [
'son',
{
person: 'person'
}
]
}
let { target, target: [x, {y}] } = obj;
// x: 'son'
// y: 'person'
// target: ['son', {person: 'person'}]
再来看另一个例子
let obj = {
person: {
son: {
car: 'BMW',
money: 'Infinty'
}
}
}
let { person, person: {son}, person: { son: {car} } }
/* person: {
son: {
car: 'BMW',
money: 'Infinty'
}
}
*/
/* son: {
car: 'BMW',
money: 'Infinty'
}
*/
// car: 'BMW'
第二个person和第三个person与son都是模式,真正被解构的是在花括号里的值
let obj = {}
let arr = []
let { foo: obj.prop, bar: arr[0] } = {prop: 'show', bar: true}
// obj: {prop: 'show'}
// arr: [true]
下面这种会报错
let { foo: {bar} } = {baz: 'baz'}
// error
原因是foo不存在,所以子对象取值会报错
let obj1 = {}
let obj2 = {foo: 'bar'}
Object.setPrototypeof(obj1, obj2);
// 此时obj2就是obj1的原型
let {foo} = obj1;
// 解构赋值可以拿到原型属性
解构赋值可以拿到原型属性
与数组一样,对象也可以有默认值
let {x = 1, y = 2} = {x: undefined,y: 5}
// x = 1
// y = 5
也就是说对象也采用了等号右边严格等于(===)undefined,默认值生效
let {x = 3} = {x: undefined}
let {y = 2} = {y: null}
// x = 3
// y = null
null不严格等于undefined所以不能使用默认值
如果一个变量已声明,不该再用于结构
let x;
{x} = {x: 1};
// 此时的{x}被js理解我一个代码块去执行,所以会报错
({x} = {x: 1});
// 此时为立即执行,不会报错
等于左边可以不放变量来进行赋值,但没有意义
({} = [true, false]);
({} = 'abc');
({} = []);
数组是本质是特殊的对象,所以可以对数组进行对象形式解构
let arr = [1, 2, 3];
let {0: first, [arr.length - 1]: last} = arr;
// first = 1
// last = 3
first代表数组的第0位,所以就是1,length - 1是第二位,所以last就是3
[arr.length - 1]属于属性名表达式
- 字符串的解构赋值
在解构的时候,字符串会被转换成类数组对象
拥有length属性
let [w, o, r, l, d] = 'hello';
// w = h
// o = e
// r = l
// l = l
// d = o
并且可以获取length属性
let [length: len] = 'hello,es6';
// len: 9
-
数值和布尔的解构
如果等号右边的值不是数组,对象就会先进行转换,转换为对象,来进行赋值
// 会先调用包装类,把123转换为对象
let {tostring: s} = 123;
let {tostring: s} = { value: 123, tostring: fn(){} }
// 所以s可以获取到原型上tostring的方法
s === Number.prototype.tostring
// boolean同上
let {tostring : b} = false;
let {tostring : b} = {value: false, tostring: fn(){}}
b === Boolean.prototype.tostring
这种解构的规定就是,如果不是对象或数组就先转换为对象,如果转不了就报错,如null,undefined
let {prop: foo} = null;
let {prop: bar} = undefined;
// 以上都会报错
- 函数参数解构
function test ([x, y]) {
return x + y
}
test([1, 9])
上述看起来就是实参与形参的传递,但在传入数组的一刻就已经进行解构了
[[1, 2], [3, 4]].map( ([a, b]) => a + b )
// 此时[1, 2]就是a [3, 4]就是b被依次解构到一个新数组中
同样函数参数也可以使用默认值
function test ({x = 0, y = 0} = {}) {
return [x, y]
}
test({x: 1, y: 2}) // [1, 2]
test({x: 3}) // [3, 0]
test({}) // [0, 0]
test() // [0, 0]
// 第一种显而易见都传值了
// 第二种只传了x所以y是默认值
// 第三种由于传了空对象,所以返回的结果是默认值
// 第四章什么都没有传,所以严格等于undefined,使用默认值
还有一个例子,给函数参数设置默认值,而不给变量x,y设置
function person ({x, y} = {x: 0, y: 0}) {
return [x, y]
}
// 此时的x,y没有默认值,因为设置的默认值是{x, y} = {x: 0, y: 0},
// 而不是{x = 0, y = 0} = {},它是在给{x, y} 这个参数在设置默认值,
// 所以x , y 是undefined,所以会有以下结果.
person({x: 3, y: 5}) // [3, 5]
person({x: 3}) // [3, undefined]
person({}) // [undefined, undefined]
person() // [0, 0]
// 以上都没有难度
// 最后一个为什么是[0, 0] 而不是[undefined, undefined]
// 由于你没传任何值,{x, y}这个参数会使用默认值{x: 0, y: 0},所以返回[0, 0]
由于undefined会触发函数参数默认值
[1, undefined, 5].map( (x = 'foo') => x)
// [1, 'foo', 5]
为什么要用解构赋值
1. 交换两个值
在以前可能要声明变量运用一些手法交换值
let a = 100;
let b = 200;
// 第一种
let c = a + b;
let a = c - a;
let b = c - b;
// 第二种
let a = a + b;
let b = a - b;
let a = a - b;
// 第三种, 也就是解构赋值的交换
[a, b] = [b, a];
// so easy to Happy啊
- 函数返回多个值
// 数组
function test () {
return [1, 2, 3]
}
let [a, b, c] = test();
// 对象
function show () {
return {
car : 'BMW',
wife : 'xiaofan'
}
}
let [car, wife] = show();
- 函数参数的定义
// 有序的数组
function person ([x, y, z]) {
return [x, y ,z]
}
person([1, 2, 3])
// 无序的对象
function son ({x = 0, y = 1, z = 2}) {
return {x, y, z
}
}
son({x: 1, y: 3, z: 9})
- 提取json
let json = {
"name" : "xiaoming",
"age" : 20,
"sex" : "male"
}
let [id, age, sex: girl] = json;
// id = "xiaoming", age = 20, girl = "male"
- 函数参数默认值
function ajax ({method = 'get', async = 'async', foo = 'foo'} = {}) {
}
// 无需再初始化变量 let foo = 'foo'这种
- 遍历map结构
let map = new Map();
map.set('car': 'BMW');
map.set('wife': 'xiaofan');
// 获取键值
for (let [key, value] of map){
console.log(key + ':' + value);
}
// 只获取键
for (let [key] of map){
console.log(key);
}
// 只获取值
for (let [, value] of map){
console.log(value);
}
// 因为这里第一个参数就是键,所以需要都好来填充,使其变为undefined
- 加载模块
import React,{ Component } from 'React';
// 上面就等于下面这样
import React from 'React';
const Component = React.Component;
解构带来了那些方便
都写这么多了还不够方便你的书写?