在 JavaScript 中,三点运算符 (...) 意味着两件事:
- 扩展运算符
- 其余运算符
当用作扩展运算符时,三点运算符扩展数组的元素,其中需要零个或多个参数。
一个典型的例子是合并两个数组时。
例如:
const boys = ['Bob', 'Charlie'];
const girls = ['Alice', 'Diana'];
const all = [...boys, ...girls];
console.log(all); // ["Bob", "Charlie", "Alice", "Diana"]
在这里,三点运算符将数组的元素传播到它们所在的位置,形成一个新数组。
扩展运算符还可以在解构过程中提取数组的元素。
例如,给定一个包含 5 个名称的列表,让我们将前三个名称分配给单独的变量,将后两个名称分配给一个数组:
const names = ["Alice", "Bob", "Charlie", "David", "Eric"];
let first, second, third, lastTwo;
[first, second, third, ...lastTwo] = names;
console.log(lastTwo); // ["David", "Eric"]
rest 运算符可以将传递给函数的参数作为数组收集。
例如,让我们创建一个汇总任意数量参数的函数:
function sum(...args) {
return args.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1)); // 1
console.log(sum(1, 2, 3, 4, 5, 6)); // 21
此时,重要的是要意识到三点运算符不仅限于在数组上使用。
您也可以将它与其他可迭代类型一起使用。
在本指南中,您将了解有关JavaScript 中的三点(...)运算符所需了解的所有信息。
这包括了解:
- 传播语法。
- 休息语法。
- 三个点解决了什么问题?
此外,您将看到有关如何在代码中使用三点运算符的实用示例。
目录
- JavaScript 中的三个点 (...)
- 传播语法
- 例子
- 使用函数传播语法
- 使用数组文字的扩展语法
- 使用对象文字的传播语法
- 休息语法
- 其余参数的限制
- 1. 只允许一个 Rest 参数
- 2. Rest 参数必须是最后一个参数
- Rest 参数解决了什么问题?
- 例子
- 具有任意数量参数的函数
- 将常规参数与休息参数结合起来
- 函数参数作为数组
- 三点运算符应用程序
- 示例 1:HTMLCollection 到数组
- 示例 2:轻松使用数学函数
- 示例 3:解构对象
- 示例 4:获取数组的浅拷贝
- 结论
- 延伸阅读
JavaScript 中的三个点 (...)
在 JavaScript 中,从 ES6 开始,在处理可迭代对象时可以使用 (...) 运算符。
三点运算符有两个有用的用途:
- 传播语法
- 其余语法
在本节中,您将详细熟悉这两个概念。
传播语法
扩展语法使扩展可迭代的元素成为可能。
换句话说,扩展运算符将可迭代元素扩展到预期零个或多个参数的地方。
在处理对象时,您可以使用扩展运算符在预期的位置扩展键值对。
让我们看一下扩展运算符在不同上下文中的语法:
在函数调用中,展开运算符如下所示:
someFunc(...iterObj); // pass all the elements of iterObj as arguments to someFunc.
使用数组文字,展开运算符的工作方式如下:
["el1", "el2", "el3", ...arr]; // add the elements of arr to the array.
对于对象字面量,展开运算符的语法如下所示:
let objCopy = { ...obj }; // Spread all key-value pairs from obj to a new object
现在您已经对扩展语法有了基本的了解,让我们看一下上面每种情况的一些示例。
例子
在上一节中,您看到了如何使用扩展语法:
- 函数调用
- 数组
- 对象
为了帮助理解,让我们看一下每个用例的示例。
使用函数传播语法
让我们实现一个接受三个参数并将它们打印出来的函数。
我们不将参数作为常规参数传递,而是将它们作为数组传递。
为此,您需要使用扩展运算符将它们转换为常规参数。
下面是它在代码中的样子:
function showCoords(x, y, z) {
console.log(`x: ${x}, y: ${y}, z: ${z}`);
}
const coords = [2, 1.5, 3.5];
showCoords(...coords); // Prints "x: 2, y: 1.5, z: 3.5"
如您所见,这非常方便。
当扩展运算符不存在时,您必须使用.apply()方法将数组元素作为参数传递给函数。
与展开运算符的简洁语法相比,这看起来相当乏味:
showCoords.apply(null, coords);
接下来,让我们看一个在修改数组时如何使用扩展语法的示例。
使用数组文字的扩展语法
将扩展运算符与数组一起使用的经典示例是当您想要组合两个数组时。
例如:
const boys = ['Bob', 'Charlie'];
const girls = ['Alice', 'Diana'];
const all = [...boys, ...girls];
console.log(all); // ["Bob", "Charlie", "Alice", "Diana"]
当然,这不是您可以对数组使用扩展语法的唯一方法。
例如,您也可以将值嵌入到数组的中间。
扩展运算符将元素从数组中拉到另一个位置。
例如:
const boys = ['Bob', 'Charlie'];
const girls = ['Alice', 'Diana'];
const all = ["Eric", ...boys, ...girls, "Gabriel"];
console.log(all); // ["Eric", "Bob", "Charlie", "Alice", "Diana", "Gabriel"]
使用对象文字的传播语法
给定两个对象,您可以使用扩展运算符合并它们:
let obj1 = { test: 'value', x: 10 };
let obj2 = { test: 'other value', y: 20 };
let combined = { ...obj1, ...obj2 };
console.log(combined); // Object { test: "other value", x: 10, y: 20 }
如您所见,组合对象由原始对象中的所有唯一键值对组成。
如果两个对象中都出现一个键,则使用最后一个对象的键值对。
凉爽的!
现在您已经对扩展运算符在 JavaScript 中的工作方式有了深入的了解。
接下来,我们来看看三点算子是如何作为余数算子使用的。
休息语法
rest 语法使得创建一个接受任意数量参数的函数成为可能。
其余语法使用三点运算符(...)。
下面是使用 rest 语法的样子:
function f(a, b, ...rest) {
// actions
}
在这里,您可以在第一个和第二个参数之后输入任意数量的参数。
显然,其余的不必称为rest。您可以给它任何其他名称,类似于在 JavaScript 中命名常规参数的方式。
例如:
function f(a, b, ...moreArgs) {
// actions
}
其余参数的限制
使用 rest 参数有两个限制:
- 休息参数只能有一个。
- 其余参数必须是函数中的最后一个参数。
让我们仔细看看这些限制。
1. 只允许一个 Rest 参数
JavaScript 函数在函数定义中只能有一个…rest参数。
例如,这种类型的函数无法编译:
function f(...args1, ...args2) {}
2. Rest 参数必须是最后一个参数
其余参数必须是函数定义中的最后一个参数。
例如,这不起作用:
function f(...rest, x, y, z) {}
但这确实:
function f(x, y, z, ...rest) {}
Rest 参数解决了什么问题?
在将剩余参数引入 JavaScript 之前,有很多样板代码用于将函数参数转换为数组。
访问函数参数的唯一方法是通过arguments对象。
但这不是一个数组。
要将参数视为数组,您需要将参数单独转换为数组。这不太实用。
这是休息参数之前和今天的时间比较:
// BEFORE
// Back in the day, "arguments" could be converted to a normal array by:
function f(a, b) {
let argsArray = Array.prototype.slice.call(arguments);
// Alternatively
// let argsArray = [].slice.call(arguments)
// let argsArray = Array.from(arguments)
let firstArg = argsArray.shift();
}
// AFTER
// These days, you do not need to make the conversion thanks to rest parameters
function f(...args) {
let firstArg = args.shift();
}
这个例子很理论化。在下面的示例中,您将了解为什么这实际上非常有用。
说到例子,让我们来看看其中的一堆。
例子
理论就够了。是时候用剩下的参数弄脏我们的手了。
具有任意数量参数的函数
如前所述,您可以使用 rest 语法创建一个接受任意数量参数的函数。
例如,让我们编写一个函数,使用reduce()方法对任意数量的参数求和。
function sum(...args) {
return args.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1)); // 1
console.log(sum(1, 2, 3, 4, 5, 6)); // 21
这是一个完美的例子,说明将参数作为数组开始是非常有用的。
现在有一种方法可以直接对参数进行操作,而不必单独将它们转换为数组。
让我们看一下其余参数的另一个示例。
将常规参数与休息参数结合起来
如您所知,对其余参数的两个限制是:
- 其余参数必须是函数定义中的最后一个参数。
- 函数定义中只能有一个剩余参数。
但是没有什么能阻止您将剩余参数与一个或多个常规参数混合。
为了演示这一点,让我们修改前面示例中的sum()函数。
这一次,让我们将函数命名为accumulate()并让它接受两个参数:
- 第一个参数指定要执行的操作。
- 最后一个参数是要操作的任意数量的值。
下面是它在代码中的样子:
function accumulate(operation, ...args) {
if (operation == "sum") {
return args.reduce((a, b) => a + b, 0);
} else if (operation == "sub") {
return args.reduce((a, b) => a - b, 0);
} else {
return "No operation";
}
}
console.log(accumulate("sum", 1, 2, 3)); // 6
console.log(accumulate("nothing", 1)); // No operation
console.log(accumulate("sub", 1, 2, 3, 4, 5, 6)); // -21
函数参数作为数组
这个例子的重点是展示拥有休息参数的真正有用性。
正如我之前提到的,其余参数的类型是Array。
这使您可以直接对函数内部的参数进行操作。
在早期版本的 JavaScript 中,您必须通过特殊的参数对象访问函数参数。
问题是参数对象不是数组。
要使其成为数组,您必须使用样板代码进行转换。
首先,让我们编写一个对任意数量的参数进行排序的 JavaScript 函数(作为剩余参数传递):
function sort(...args) {
return args.sort();
}
console.log(sort(5, 3, 6, 2)); // prints 2, 3, 5, 6
这段代码完美地工作,因为它直接将参数排序为数组。
现在让我们回到没有剩余参数的时间。
这是不使用剩余参数的sort()函数的一个版本:
function sort() {
return arguments.sort();
}
console.log(sort(5, 3, 6, 2)); // throws a TypeError (arguments.sort is not a function)
如您所见,这段代码会导致错误。
该错误是由arguments对象不是数组引起的。
要解决此问题,您必须将参数转换为单独一行中的数组:
function sort() {
let args = Array.from(arguments);
let sortedArgs = args.sort();
return sortedArgs;
}
console.log(sort(5, 3, 6, 2)); // 2, 3, 5, 6
如您所见,这种方法比使用其余参数的方法更冗长。
至此,您已经很好地理解了扩展/休息语法在 JavaScript 中是如何工作的。您已经阅读了理论并看到了一些基本示例。
在你走之前,我想向你展示一堆 JavaScript 中三点运算符的实际应用。
三点运算符应用程序
让我们通过查看扩展/休息语法的一些巧妙应用来完成本教程。
示例 1:HTMLCollection 到数组
要使用 JavaScript 对HTMLCollection执行操作,您需要将HTMLCollection转换为数组。
因为HTMLCollection是一个可迭代类型,所以您可以在其上使用扩展运算符。
因此,您可以使用扩展运算符来使用HTMLCollection元素填充数组。这会将HTMLCollection转换为 HTML 元素数组。
例如:
let elemsArr = [...document.getElementsByTagName("div")];
elemsArr包含页面上所有 div 标记的数组。
现在您可以毫无问题地对数组中的 div 执行操作。
示例 2:轻松使用数学函数
要在 JavaScript 中计算最大值,可以使用Math.max函数。要获得最小值,请使用Math.min函数。
但是,这些函数的行为与您的预期略有不同。
让我告诉你我的意思:
let numbers = [1, 4, 2, 10, 6, -1];
let min = Math.min(numbers);
let max = Math.max(numbers);
console.log(min, max); // Prints NaN, NaN
这些函数不是查找最小值/最大值,而是返回NaN。
这是因为这些函数不需要可迭代的参数。相反,他们希望将数字作为单独的参数接收。
例如:
let min = Math.min(1, 4, 2, 10, 6, -1);
let max = Math.max(1, 4, 2, 10, 6, -1);
console.log(min, max); // -1, 10
现在函数成功地找到了极值。
但是如果你有一个数字数组呢?
要查找数字数组的最大值/最小值,请使用扩展运算符。
正如您所了解的,展开运算符获取数组元素并将它们添加到预期零个或多个参数的位置。
例如:
let numbers = [1, 4, 2, 10, 6, -1];
let min = Math.min(...numbers);
let max = Math.max(...numbers);
console.log(min, max); // Prints -1, 10
示例 3:解构对象
正如您已经了解到的,您也可以对对象使用扩展运算符。
例如,给定一个表示学生数据的对象,您可以使用扩展运算符将剩余数据抓取到一个单独的变量中。
例如:
let student = {
name: "Alice",
age: 32,
address: "Imaginary Street 9001",
allergies: "Peanuts"
}
let {name, age, ...otherInfo} = student;
console.log(name); // Alice
console.log(age); // 32
console.log(otherInfo); // { address: "Imaginary Street 9001", allergies: "Peanuts" }
}
示例 4:获取数组的浅拷贝
在 JavaScript 中,赋值运算符 ( = ) 创建对原始对象的引用。
通过一个简单的示例可以很容易地看出这一点:
let arr1 = [1, 2, 3];
let arr2 = arr1
arr1[0] = 1000;
console.log(arr1); // [1000, 2, 3]
console.log(arr2); // [1000, 2, 3]
即使我们改变了arr1的第一个值,arr2的第一个值也改变了。
这是因为arr2指向与arr1相同的内存地址。
换句话说,您不能通过将数组分配给另一个数组来复制它。
相反,您可以使用扩展运算符创建数组的副本。
例如:
let arr1 = [1, 2, 3];
let arr2 = [...arr1];
arr1[0] = 1000;
console.log(arr1); // [1000, 2, 3]
console.log(arr2); // [1, 2, 3]
这是因为扩展运算符从原始数组中挑选元素并用它们填充一个新数组。
请记住,这会创建一个浅拷贝!
这意味着复制的数组元素仍然引用原始值。
如果您处理数字,这不是问题。
但是,如果您有一个数组数组,则复制的数组元素仍然引用原始数组元素。换句话说,更改元素数组既发生在复制的数组中,也发生在原始数组中。×
结论
今天您学习了如何在 JavaScript中使用三点运算符 ( … )。
回顾一下,这三个点可能意味着以下两种情况之一:
- 扩展运算符
- 其余运算符
展开运算符可以将可迭代元素展开到需要一个或多个参数的地方。
例如,您可以通过将数组的元素展开为一个来组合两个数组。
使用函数时,rest 运算符很有用。
它使创建接受任意数量参数的函数成为可能。
此外,要直接将函数参数作为数组接收,您可以使用 rest 参数。在休息参数之前,您需要编写许多不必要的代码来将函数参数转换为数组。