函数剩余参数和展开运算符

三葉Leaves Author

函数剩余参数 (Rest Parameters)、动态参数 (Arguments Object)

剩余参数

  • 剩余参数必须是最后一个形参。你不能在剩余参数后面再定义其他参数。
  • 收集到的是一个真正的数组 (Array instance),这意味着你可以直接使用所有数组方法 (如 map, filter, reduce, forEach 等),非常方便。

直接两个例子来说明:

处理任意参数数量的求和函数

1
2
3
4
5
6
7
8
9
10
11
12
13
function sumAll(...numbers) {
// 'numbers' 就是一个真正的数组
console.log(numbers); // 例如:[1, 2, 3] 或 [10, 20, 30, 40, 50]
let total = 0;
for (const num of numbers) {
total += num;
}
return total;
}

console.log(sumAll(1, 2, 3)); // 输出: [1, 2, 3] 和 6
console.log(sumAll(10, 20, 30, 40, 50)); // 输出: [10, 20, 30, 40, 50] 和 150
console.log(sumAll()); // 输出: [] 和 0

参数用处不同的一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function greet(greeting, ...names) {
// 'greeting' 是第一个参数
// 'names' 收集了从第二个开始的所有参数
console.log(greeting);
console.log(names); // 例如:['Alice', 'Bob', 'Charlie']
names.forEach(name => console.log(`${greeting}, ${name}!`));
}

greet("Hello", "Alice", "Bob", "Charlie");
// 输出:
// Hello
// ["Alice", "Bob", "Charlie"]
// Hello, Alice!
// Hello, Bob!
// Hello, Charlie!

greet("Hi", "Dave");
// 输出:
// Hi
// ["Dave"]
// Hi, Dave!

动态参数

仅作了解,现代开发一般不用

在 ES6 的剩余参数出现之前,JavaScript 函数内部有一个特殊的对象叫做 arguments,是个伪数组。它可以用来访问函数调用时传入的所有实际参数,无论这些参数是否在函数定义中声明。

  • arguments 对象是类数组对象 (array-like object),不是真正的数组。它有 length 属性和索引访问,但没有数组的内置方法 (如 map, filter 等)。
  • 箭头函数 (Arrow Functions) 中,arguments 对象是不可用的。箭头函数会从其包含的(非箭头)函数中继承 arguments 对象,或者在全局作用域中是 undefined。这是推荐使用剩余参数的一个重要原因。
  • 在严格模式 ('use strict')下,arguments 对象的一些特性会发生改变(例如,它的属性值不会再与对应的命名参数保持同步)。

展开运算符 (Spread Operator)

对下面这句话的理解越深刻,越能巧妙使用展开运算符:

核心思想:将一个聚合的数据结构(如数组或对象)打散成独立的元素或键值对。

有下面几个主要作用:

在函数调用时展开数组

数组没有自带的求最大值的方法,但是用这个展开运算符,问题会变得很简单。

下面这个例子中,展开运算符会将数组的每个元素作为单独的参数传递给 Math.max():

1
2
const numbers = [1, 5, 2, 8]; console.log(Math.max(...numbers)); // 等价于 Math.max(1, 5, 2, 8),输出: 8
console.log(Math.max(numbers)); // 错误!Math.max 不接受数组作为参数

如果强行拼凑出 max 函数需要的格式,其并不能接受:

1
2
3
4
5
const arr = [1, 2, 3, 4, 5]

console.log(arr.join(','));

console.log(Math.max(arr.join(','))); //NaN

由于字符串 “1,2,3,4,5” 无法直接转换为一个有效的数字,所以返回 NaN。

构建新数组 (数组字面量)

可以用来合并数组或在现有数组中插入元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// 合并数组
const combinedArray = [...arr1, ...arr2];
console.log(combinedArray); // 输出: [1, 2, 3, 4, 5, 6]

const arr3 = [0, ...arr1, 4, ...arr2, 7];
console.log(arr3); // 输出: [0, 1, 2, 3, 4, 4, 5, 6, 7]

// 复制数组 (浅拷贝)
const originalArray = [10, 20, 30];
const copiedArray = [...originalArray];
console.log(copiedArray); // 输出: [10, 20, 30]
copiedArray.push(40);
console.log(originalArray); // 输出: [10, 20, 30] (原数组不受影响)
console.log(copiedArray); // 输出: [10, 20, 30, 40]

展开字符串

字符串是可迭代的,所以可以被展开成单个字符。

1
2
3
const str = "Hello";
const chars = [...str];
console.log(chars); // 输出: ["H", "e", "l", "l", "o"]

在对象字面量中展开对象 (ES2018+)

可以用来合并对象或复制对象属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const obj1 = { name: "Alice", age: 30 };
const obj2 = { city: "New York", occupation: "Engineer" };

// 合并对象
const mergedObject = { ...obj1, ...obj2 };
console.log(mergedObject);
// 输出: { name: "Alice", age: 30, city: "New York", occupation: "Engineer" }

// 如果有相同属性名,后面的会覆盖前面的
const obj3 = { name: "Bob", country: "USA" };
const updatedObject = { ...obj1, ...obj3 }; // obj3.name ("Bob") 会覆盖 obj1.name ("Alice")
console.log(updatedObject);
// 输出: { name: "Bob", age: 30, country: "USA" }

// 复制对象 (浅拷贝)
const originalObj = { a: 1, b: 2 };
const copiedObj = { ...originalObj };
console.log(copiedObj); // 输出: { a: 1, b: 2 }
copiedObj.c = 3;
console.log(originalObj); // 输出: { a: 1, b: 2 }
console.log(copiedObj); // 输出: { a: 1, b: 2, c: 3 }

关键点

  • 展开运算符用于可迭代对象 (如数组、字符串、Map、Set) 或 (从 ES2018 开始) 对象
  • 在函数调用中,它将数组元素作为独立参数传入。
  • 在数组字面量中,它将一个数组的元素插入到新数组中。
  • 在对象字面量中,它将一个对象的自有可枚举属性复制到新对象中 (属性拷贝是浅拷贝)。
  • 标题: 函数剩余参数和展开运算符
  • 作者: 三葉Leaves
  • 创建于 : 2025-06-01 00:00:00
  • 更新于 : 2025-06-07 19:46:13
  • 链接: https://blog.oksanye.com/a9aa03f6ba6d/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论