在学习JS的过程中, ...这个语法总是在大大简化代码的同时也提升了理解难度。本文先介绍了解构赋值,并介绍了解构赋值中...作为剩余语法的作用,之后介绍了...作为展开语法的作用。

解构赋值

解构赋值是对ObjectArray进行简化操作的一种特殊语法,让我们可以把数组或对象拆到一系列的变量之中。

数组解构

1
2
3
4
let arr = ["LLLeo", "Li"];
let [firstName,surname] = arr;
alert(firstName); // LLLeo
alert(surname); // Li

解构赋值与下面的写法是等价的:

1
2
let firstName = arr[0];
let surname = arr[1];

实际上,这里的“数组”可以拓展为任何的可迭代对象(通过Symbol.iterator实现了迭代功能),比如Set,Map等。

1
2
3
4
5
6
let user = new Map();
user.set("name", "John");
user.set("age", "30");
for(let [key,,index] of user){
alert(`${key}:${value}`);
}

JS中著名的交换两个变量值的技巧(Python中也有类似的操作,还不用写[]):

1
[a,b] = [b,a];

默认值:

1
let [name = "Guest", surname = "Anonymous"] = ["Julius"];

左侧可以是任何LVal

1
2
let user = {};
[user.name, user.surname] = "John Smith".split(' ');

数组解构中的...

1
2
let [a, b, ...c] = ['1','2','3','4','5'];
alert(c.join("")); //345

...表示获取其余数组项,使用时必须位于解构赋值的最后一个参数位置。

对象解构

1
let {var1, var2} = {var1:…, var2:…}

变量的顺序并不重要,我们还可以对变量进行重命名和默认赋值:

1
2
let {height : h, width : w = 1000, title : t = "舞!舞!舞!"} = { title: "寻🐏冒险记", height: 200, width: 100 }
alert(h); // 200

默认赋值只在没有提供对应值时调用,同时默认值可以是任意表达式或者函数调用。

1
let {width = prompt("width?"), title = prompt("title?")} = options;

对象同时支持嵌套解构,但需要结构一一对应。

对象解构中的...

对象解构中的...可以获取到对象的剩余属性。

1
2
3
let options = { title: "寻🐏冒险记", height: 200, width: 100 }
let {title, ...rest} = options;
// 现在的 rest值为 { height: 200, width: 100 }

实际应用——智能函数参数

开发中很多函数都需要很多参数,如果我们把函数声明都写成:

1
2
3
function showMenu(title = "无", width = 200, height = 100, items = []) {
// ...
}

为萝卜们填坑的过程将会造成很大的记忆负担,同时可能传递很多undifined,浪费编程时间。

我们可以只传递一个对象,然后通过解构赋值的方式来把传递的对象解构成各个参数:

1
2
3
4
5
6
7
8
let options = {
title: "Title",
items: ["Item1", "Item2"]
};
function showMenu({title = "无", width = 200, height = 100, items = []}) {
// ...
}
showMenu(options)

虽然已经大大简化了函数传参,但是当我们直接进行showMenu()调用时还会报错。

1
2
3
function showMenu({title = "无", width = 200, height = 100, items = []} = {}) {
// ...
}

我们通过指定空对象为参数对象默认值来解决问题。

展开语法

  • 展开语法可以在函数调用/数组构造时,将数组表达式或者String在语法层面展开。
  • 展开语法在构造字面量对象时,可以将对象表达式按键值对形式展开。

展开语法与Object.assign()的行为一致,都是执行了浅拷贝。

同样,这里的“数组”可以拓展为可迭代对象。

函数调用 / Rest参数 / 数组构造

函数调用:

1
2
3
4
5
6
7
8
9
const Func1 = (x,y,z)=>{
// ...
};
let args = [1,2,3];
let a = [1,2];
let str = "12";
Func1(...args);
Func1(...a,...[3]);
Func1(...str,"3");

函数Rest参数:

1
2
3
4
5
6
7
8
9
10
11
function sumAll(...args) { // 数组名为 args
let sum = 0;

for (let arg of args) sum += arg;

return sum;
}

alert( sumAll(1) ); // 1
alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6

数组构造:

1
2
3
let args = ['123', '456'];
let nums = ['111', ...parts, '777', '888'];
alert(nums); // ["111", "123", "456", "777", "888"]

使用展开语法可以替代一些数组操作:

1
2
3
4
5
6
let arr1 = [1,2,3];
let arr2 = [4,5,6];
//concat
let arr3 = [...arr1,...arr2];
// unshift
let arr4 = [...arr2,...arr1];

字面量对象

1
2
3
4
5
6
7
8
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };

let clonedObj = { ...obj1 };
// 克隆后的对象: { foo: "bar", x: 42 }

let mergedObj = { ...obj1, ...obj2 };
// 合并后的对象: { foo: "baz", x: 42, y: 13 }

参考:

展开语法 - JavaScript | MDN (mozilla.org)

解构赋值 (javascript.info)

Iterable object(可迭代对象) (javascript.info)

数组方法 (javascript.info)