1、数组的解构赋值
基本用法
本质上,只要等号两边的模式相同,左边的变量就会被赋予对应的值,...表示将剩下的值赋予此变量,无剩余则赋值为空数组,解构不成功则赋值为undefined。等号两边都要为数组。
let a = 1;
let b = 2;
let c = 3;
//ES6可以写成
let [a, b, c] = [1, 2, 3];
//多重数组的例子
let [a, [[b], c]] = [1, [[2], 3]];
a // 1
b // 2
c // 3
//对应位置赋予对应的值
let [ , , c] = ["1", "2", "3"];
c // "3"
let [a, , c] = [1, 2, 3];
a // 1
c // 3
//...语法有剩余值时
let [a, ...z] = [1, 2, 3, 4];
a // 1
z // [2, 3, 4]
//...语法无剩余值时
let [a, b, ...z] = ['a'];
a // "a"
b // undefined
z // [] 空数组
默认值
解构赋值可以使用默认值,当且仅当变量的值全等于(===)undefined时,才会使用默认值,即使是等于null时也不会使用默认值。
let [a = 1] = []; a // 1
let [a, b = '2'] = ['1']; // a='1', b='2'
let [a, b = '2'] = ['1', undefined]; // a='1', b='2'
let [x = 1] = [null]; x // null
当默认值是一个表达式的时候(例如一个行数表达式),若变量能够取到值(值不全等于undefined),则表达式不会执行。
function f() {
console.log('a');
}
let [x = f()] = [1]; //f()不会执行,y=1
默认值可以引用解构赋值中的其他变量,前提是变量已经声明
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; // y在x引用的时候还没有声明,所以报错。
2、对象的解构赋值
与数组的解构赋值不同的是,数组解构是按照元素的次序赋值的,而对象则不受次序的影响,变量必须与属性同名,才能被赋值,没有同名属性则赋值为undefined。
let { pro1, pro2 } = { pro1: "1", pro2: "2" };
pro1 // "1"
pro2 // "2"
//上述表述是一种缩写,其本质是
let { pro1:pro1, pro2:pro2 } = { pro1: "1", pro2: "2" };
//在等号左边时,:号左边的pro1和pro2是定义匹配模式,真正被赋值的是:号右边的pro1和pro2,当匹配模式和属性名同名时,可以使用缩写
let { proa:prox, prob:proy } = { proa: "1", prob: "2" };
prox //"1"
proy //"2"
对象解构中左边的变量一定是由:组成的一对匹配模式和属性名。只有一个匹配模式时,一定是匹配模式和属性名相同,使用了简写。
var obj= {
a: {
b: {
c: 1,
d: 2
}
}
};
var { a: { b: { c }} } = obj;
a // 报错,a在:左边,是匹配模式,不是属性名
b // 报错,b在:左边,是匹配模式,不是属性名
c // 1
对象的解构赋值同样支持默认值,规则与数组的解构相似:
- 当且仅当变量值全等于(===)undefined时,才会取默认值。
- 如果解构失败,变量的值等于undefined。
当大括号位于本行的首位时,需要加上小括号,因为 { } 表示一个代码块,不是一个单独的语句。
let x;
{x} = {x: 1}; // 语法错误,左边是一个代码块,不是一个声明。
({x} = {x: 1}); // 加上小括号,成为一个单独的语句,语法正确。
当变量名与现有对象的属性名相同时,可以直接使用对象赋值。
let { log, sin, cos } = Math;
log // Math.log
sin // Math.sin
cos // Math.cos
因为在JavaScript中,数组是特殊的对象,所以可以用数组进行对象属性的结构。
let arr = [1, 2, 3];
let {0 : a, [arr.length - 1] : b} = arr;
a // 1
b // 3
3、字符串的解构赋值
字符串进行结构赋值时,被转化为了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';
a // 'h'
let {length : len} = 'hello';
len // 5 因为右边'hello'字符串被转化为了一个类似数组的对象,具有length属性,所以可以直接赋值给左边同名的匹配模式。
4、数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
解构赋值的规则是,只要右边不是对象或者数组,就将其转化为对象,再进行匹配赋值。
因为undefined和null不能转化为对象,所以单独将这两个值当做右边的值进行匹配赋值时会报错。
let { prop: x } = undefined; // 报错
let { prop: y } = null; // 报错
5、函数参数的解构赋值
function add([x, y]){
return x + y;
}
add([1,2]); // 传输数组参数相当于执行了 [x,y]=[1,2];x和y被分别赋值为1和2
函数参数的解构也可以使用默认值,但下面两种情况不同:
function move({x = 0, y = 0} = {}) {
return [x, y];
}
这种方式表示x和y没有给定值时默认值为0,x和y是分开赋值的,只要参数中存在没有传入的项,就使用其默认值。
即只要某一项参数不存在,就一定会使用其默认值。
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
这种方式指定的是整个参数的默认值,而不是单独指定x和y,x和y是当做一个整体。只有当完全不传任何参数时,才会使用默认值(传入空对象也不会使用默认值),无论是单独传了x参数,还是单独传了y参数,未传的项都不会使用默认值。
即只要存在参数,且参数不全等于(===)undefined,就一定不会使用默认值。
6、圆括号问题
ES6的规则是:只要有可能导致解构歧义,就不得使用圆括号。
所以,在非必要情况下,没有必要添加圆括号(事实上大部分圆括号都是没有必要添加的)。
不允许使用圆括号的情况
- 声明语句中不允许使用圆括号
- 函数参数中,匹配模式不允许使用圆括号,因为函数参数也属于变量声明
- 不能将整个模式或嵌套中的某一整层放在圆括号当中。
7、解构用途
交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x]; // 简洁易懂的交换
从函数返回多个值
当函数需要返回多个值时,将这些值组成一个数组或对象,返回这个数组或对象,然后再用解构赋值来取的各项的值。
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
函数参数的定义(包括函数参数使用默认值)
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x=0, y=0, z=0}) { ... }
f({z: 3, y: 2, x: 1});
提取JSON数据
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]