rest 参数和 spread 运算符
rest 参数(...args)用于收集多个参数为数组,spread 运算符(...arr)用于将数组展开为独立参数,两者语法相同但用途相反。
#resource / javascript
#resource / ecmascript
#type / concept
#status / evergreen
[!info] related notes
- 所属 MOC: ecmascript-moc, ES6 新特性 MOC
- 前置概念: ecmascript-functions, ecmascript-collection-reference-types
- 相关概念: currying, function-length, arguments-object
- 对比概念: arguments-object
rest 参数和 spread 运算符
一句话定义
... 运算符有两种用途:rest 参数用于函数定义时收集多个参数为数组,spread 运算符用于函数调用或数组/对象字面量中将数组展开为独立元素。
核心机制
Rest 参数(剩余参数)
用于函数定义时,将剩余的参数收集到一个数组中:
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
sum(1, 2, 3, 4, 5); // 15
Spread 运算符(展开运算符)
用于函数调用或数组/对象字面量中,将数组/对象展开为独立的元素:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 数组合并
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// 函数调用
function add(a, b, c) {
return a + b + c;
}
add(...arr1); // 6
// 对象合并
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const mergedObj = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 }
详细对比
| 特性 | Rest 参数 | Spread 运算符 |
|---|---|---|
| 位置 | 函数定义的参数列表 | 函数调用、数组/对象字面量 |
| 用途 | 收集多个参数为数组 | 展开数组/对象为独立元素 |
| 语法 | function fn(...args) | fn(...args) 或 [...arr] |
| 结果 | 创建新数组 | 不创建新数组,只是展开 |
实际应用
1. 参数处理
// Rest 参数:收集剩余参数
function logFirst(first, ...rest) {
console.log('第一个参数:', first);
console.log('剩余参数:', rest);
}
logFirst(1, 2, 3, 4);
// 第一个参数: 1
// 剩余参数: [2, 3, 4]
2. 数组操作
// Spread:复制数组
const original = [1, 2, 3];
const copy = [...original];
// Spread:合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]
// Spread:在数组中插入元素
const arr = [1, 2, 3];
const newArr = [0, ...arr, 4]; // [0, 1, 2, 3, 4]
3. 函数调用
// Spread:将数组作为参数传递
function add(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
add(...numbers); // 6
// 等价于
add(1, 2, 3); // 6
4. 对象操作
// Spread:复制对象
const original = { a: 1, b: 2 };
const copy = { ...original };
// Spread:合并对象
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 }
// Spread:更新对象属性
const user = { name: 'Alice', age: 25 };
const updatedUser = { ...user, age: 26 }; // { name: 'Alice', age: 26 }
与 arguments 对象的区别
Rest 参数的优势
- 是真正的数组:可以直接使用数组方法
- 箭头函数可用:箭头函数没有
arguments,但可以使用 rest 参数 - 更清晰:明确表示”剩余参数”
// 使用 arguments(类数组对象)
function sumArgs() {
return Array.from(arguments).reduce((a, b) => a + b, 0);
}
// 使用 rest 参数(真正的数组)
function sumRest(...args) {
return args.reduce((a, b) => a + b, 0);
}
// 箭头函数只能用 rest 参数
const sumArrow = (...args) => args.reduce((a, b) => a + b, 0);
何时使用哪个
| 场景 | 推荐 | 原因 |
|---|---|---|
| 现代代码 | Rest 参数 | 更清晰、是真正的数组 |
需要 this 绑定 | Rest 参数 | 箭头函数配合 rest 参数 |
| 兼容旧代码 | arguments | 旧浏览器可能不支持 rest 参数 |
常见误解
1. Rest 参数必须是最后一个参数
// ❌ 错误:rest 参数后面不能有其他参数
function fn(...args, last) {} // SyntaxError
// ✅ 正确:rest 参数必须是最后一个
function fn(first, ...rest) {}
2. 函数 length 属性不包括 rest 参数
function fn(a, b, ...rest) {}
console.log(fn.length); // 2,不包括 rest 参数
3. Spread 不限于数组
// 字符串展开
const str = 'hello';
const chars = [...str]; // ['h', 'e', 'l', 'l', 'o']
// NodeList 展开
const divs = document.querySelectorAll('div');
const divArray = [...divs];
BFE.dev 相关题目
- 1. implement curry():使用 spread 运算符合并参数
- 19. this:理解函数调用时的参数传递
面试要点
- 语法区别:知道 rest 参数用于定义,spread 用于调用/展开
- 数组特性:rest 参数是真正的数组,不是类数组对象
- 位置限制:rest 参数必须是最后一个参数
- length 属性:rest 参数不计入函数的 length 属性
- 箭头函数:箭头函数可以使用 rest 参数,但不能使用 arguments