解构赋值分类:
- 对象的解构赋值;
- 数组的解构赋值;
- 字符串的解构赋值;
- 数值和布尔值的解构赋值;
- 函数参数的解构赋值;
细节问题:
- 圆括号;
- 用途;
解构赋值分类
一.对象的解构赋值
let { name, age } = { name: "mike", age: 11 };
console.log(name); //mike
console.log(age); //11
- 对象的解构不同于数组。数组中的变量的取值是由他的位置决定的,而对象中的变量必须与属性同名才能取得值:
let { name, age } = { age: 18, name: "mike" };
console.log(name); //mike
console.log(age); //18
let { bar } = { age: 11, name: "tom" };
console.log(bar); //undefined
2.如果变量的名字和属性的名字不一致,应该这么写:
let { name: N } = { name: "tom" };
console.log(N); //tom
let obj = { first: "hello", last: "world" };
let { first: F, last: L } = obj;
console.log(F,L); //hello world
实际上,对象的解构赋值的内部机制是:先找到同名属性,然后再赋值给对应的变量。真正被赋值的是后者。上面的代码中first是匹配的模式,F是赋值的变量。
3.和数组一样,解构也可以用于嵌套结构的对象:
let obj = {
p: [
"hello",
{
y: "world"
}
]
};
let { p, p: [ x, { y }] } = obj;
console.log(x, y); //hello world
console.log(p); //["hello", { y: "world" }]
//注意:上面第二个对y属性的解构赋值中,x和y是变量,p是模式
4.如果要将一个已经声明的变量用于解构赋值,必须非常小心。
let x;
{ x } = { x: "mike" };
console.log(x); //报错
上面的代码会报错是因为JavaScript引擎会将{ x }理解成一个代码块,从而发生语法错误。正确的做法是将上面的解构赋值代码用一个圆括号包起来。
let x;
({ x } = { x: "mike" });
console.log(x); //mike
例子:
let obj = {};
let arr = [];
({ name: obj.prop, age: arr[0] } = { name: "tom", age: 15 });
console.log(obj); //{ prop: "tom" }
console.log(arr); //[15]
5.对象的解构使用默认值:
var {x = 1} = {}; //x=1
var {x, y = 5} = {x: 1}; //x=1,y=5
var {x: y = 3} = {}; //y=3
var {x: y = 7} = {x: 8}; //y=8
6.默认值生效的条件是对象的属性值严格等于undefined:
var {x = 14} = {x: undefined}; //14
var {y = 15} = {y: null}; //null
7.如果解构失败,变量的值为undefined
let {foo} = {bar: "mike"};
console.log(foo); //undefined
8.如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错。
let {foo: { one }} = {bar: "mike"}; //报错
9.对象的解构赋值,可以很方便的把现有对象的方法,赋值给变量。
let {cos, sin, tan} = Math;
10.由于数组本身是特殊的对象,所以可以对数组进行对象属性的解构。
let arr = [1, 2, 3];
let {0: first, [arr.length-1]: last} = arr;
console.log(first); //1
console.log(last); //3
二.数组的解构赋值
1.数组中的变量按照位置的顺序取值:
let [a, b, c] = [1, 2, 3];
console.log(a,b,c); //1 2 3
模式匹配:只要等号两边的模式相同,左边的变量就会被赋予对应的值。
2.嵌套数组进行解构:
let [one, [[two], three] ] = [7, [[2], 3] ];
console.log(one,two,three); //7 2 3
let [ , , third] = ["a","b","c"];
console.log(third); //c
let [ x, , z ] = [1, 2, 3];
console.log(x,z); //1 3
let [a, ...arr] = [1, 2, 3, 4];
console.log(a); //1
console.log(arr); //[2, 3, 4]
let [a, b, ...c] = ["a"];
console.log(a); //a
console.log(b); //undefined
console.log(c); //[]
3.如果解构不成功,变量的值就是undefined
let [foo] = [];
let [bar, foos] = [1];
console.log(foo, bar, foos); //undefined 1 undefined
4.如果等号的右边不是数组,那么将会报错:
// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
5.数组结构赋值也可以使用默认值:
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
同理,数组成员严格等于undefined,默认值才会生效。
6.如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
function f() {
console.log('aaa');
}
let [x = f()] = [1];
上面代码中,因为x
能取到值,所以函数f
根本不会执行。
7.默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
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: y is not defined
上面最后一个表达式之所以会报错,是因为x
用y
做默认值时,y
还没有声明。
三.字符串的解构赋值
1.字符串的解构赋值是将字符串转换为一个类似数组的对象
let [a, b, c, d, e] = "hello";
console.log(a); //h
console.log(b); //e
console.log(c); //l
console.log(d); //l
console.log(e); //o
2.类似数组的对象都有一个length属性,因此可以对这个属性进行解构赋值
let {length: len} = "hello";
console.log(len); //5
四.数值和布尔值的解构赋值
1.解构赋值时,如果等号右边是数值或布尔值,则会先转为对象
let {toString: str1} = 123;
console.log(str1 === Number.prototype.toString); //true
let {toString: str2} = true;
console.log(str2 === Boolean.prototype.toString); //true
上面代码中,数值和布尔值的包装对象都有toString
属性,因此变量s
都能取到值。
2.解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined
和null
无法转为对象,所以对它们进行解构赋值,都会报错。
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
五.函数参数的解构赋值
1.函数的参数也可以使用解构赋值
function add([x, y]){
console.log(x + y);
}
add([1, 2]); //3
2.函数参数的解构也可以使用默认值
function move({x = 0, y = 0} = {}){
console.log([x, y]);
}
move({x: 1, y: 2}); //[1, 2]
move({x: 1}); //[1, 0]
move({}); //[0, 0]
move(); //[0, 0]
3.为函数move
的参数指定默认值,而不是为变量x
和y
指定默认值
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]
细节问题
一.圆括号
1.可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。
[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确
上面三行语句都可以正确执行,因为首先它们都是赋值语句,而不是声明语句;其次它们的圆括号都不属于模式的一部分。第一行语句中,模式是取数组的第一个成员,跟圆括号无关;第二行语句中,模式是p
,而不是d
;第三行语句与第一行语句的性质一致。
二.用途
1.交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x, y); //2 1
2.从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。
//函数返回数组
function arr(){
return [1, 2, 3];
}
let [x, y, z] = arr();
console.log(x, y, z); //1 2 3
//函数返回对象
function obj(){
return {
name: "mike",
age: 11
}
}
let {name: N, age: A} = obj();
console.log(N, A); //mike 11
3.函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
4.提取json数据
解构赋值对提取 JSON 对象中的数据,尤其有用。
let jsonData = {
id: 11,
name: "tom",
number: [4, 5]
}
let {id, name, number: N} = jsonData;
console.log(id, name, N); //11 tom [4, 5]