先记住这个答案
剩余参数(...args)将额外实参收集为真正的 Array 实例,可直接使用 map、filter 等方法;arguments 是类数组对象,必须先用 Array.from 或展开符转换。剩余参数只包含未被命名参数占用的实参,且不包含 callee 属性;而 arguments 包含全部实参(包括已命名部分),且与非严格模式下的简单参数存在索引同步。
- 剩余参数是数组,arguments 是类数组
- 剩余参数只收未命名参数,arguments 包含全部
- 剩余参数必须放最后且只能一个
数组性与收集范围的机制差异
函数声明 function f(a, ...rest) 会把从第二个实参开始的所有值放入名为 rest 的真实数组。这个数组继承自 Array.prototype,因此 rest.map、rest.sort 可直接调用,无需 Array.prototype.slice.call 这类样板代码。arguments 是类数组对象,它虽然有 length 和索引,但不继承 Array.prototype,没有 sort、map 等数组修改方法,直接调用 arguments.sort 会抛出 TypeError。
另一个关键是收集范围。arguments 包含调用时传入的全部实参,包括已命名参数对应的值;而剩余参数只收集位于命名参数之后的那些实参。例如 function f(a, ...bs) 调用 f(1,2,3) 时,arguments[0] 是 1,bs 是 [2,3]。这种差别使剩余参数语义更聚焦:明确区分“已知参数”和“可变部分”,避免在函数体内再通过索引跳过固定参数。
function diff(a, ...rest) {
console.log('rest is array?', Array.isArray(rest));
console.log('arguments is array?', Array.isArray(arguments));
console.log('rest length', rest.length);
console.log('arguments length', arguments.length);
console.log('sort works?', rest.sort()[0], typeof arguments.sort);
}
diff(3, 1, 2);查看输出与解释
rest is array? true
arguments is array? false
rest length 2
arguments length 3
sort works? 1 undefined运行显示剩余参数是数组,arguments不是。arguments.sort 是 undefined,因为对象没有数组方法。
构造累加器:避免转换样板代码
假设需要实现一个计算任意数量数字总和的函数,但要求跳过第一个参数作为基数。用剩余参数可以这样写:function sumFrom(base, ...nums) { return nums.reduce((acc, n) => acc + n, base); }。调用 sumFrom(100, 1, 2, 3) 返回 106。这里 nums 直接是数组,能立即调用 reduce,代码直观。
若改用 arguments,必须先取出基数和剩余部分:function sumFrom(base) { var args = Array.prototype.slice.call(arguments, 1); return args.reduce(...); } 或者用 Array.from(arguments).slice(1)。额外转换步骤不仅啰嗦,还容易忘记索引偏移导致取错参数。剩余参数让代码表达“可变部分”的意图,减少出错风险。
语法限制和参数别名同步问题
剩余参数有限制:必须作为最后一个形参;一个函数只能有一个;不能有默认值。这些限制在语法层面就约束了不合理用法。但 arguments 没有任何位置限制,可以出现在任意位置,而且在非严格模式下,简单参数的 arguments 索引值会与命名参数保持同步——修改 arguments[0] 会改变 a,反之亦然。这种隐式同步容易让人困惑,而剩余参数是每次调用时独立创建的数组,不会与命名参数同步。
当函数需要同时使用默认参数或解构时,剩余参数的语法限制可能带来额外注意。例如 function f(a = 1, ...rest) 是合法的,但 function f(...rest, a) 会报错。如果确实需要处理“前若干参数不定、后有固定参数”的情况,剩余参数无法直接表达,只能用 arguments,然后在内部通过长度判断。这时应权衡:追求代码一致性时优先用剩余参数,但遇到非尾部的集合收集时,arguments 仍是必要工具。
容易答错的地方
- 认为 arguments 可以像数组一样调用方法
- 直接写
arguments.map(...)会报 TypeError,因为 arguments 是类数组对象,不继承 Array 原型。必须用Array.from(arguments)或展开符转换后才能使用数组方法。 - 认为剩余参数包含所有实参
- 剩余参数只包含未被命名参数占用的部分。若函数定义
function f(a, ...rest),调用f(1,2,3)时rest是[2,3],不包含1。而arguments包含全部三个值。两者长度不一定相等。
面试官还会怎么问?
剩余参数可以解构吗?
可以。比如 function f(...[a, b]) 会丢弃第三个及以后的实参,但这种方式与参数绑定语义混合,可读性差,实际中多用整体数组再解构。
默认参数与剩余参数能共存吗?
能,但剩余参数必须最后。例如 function f(a = 5, ...rest) 合法,这表示如果第一个实参未传则用默认值。注意剩余参数本身没有默认值语法。
箭头函数中如何使用 arguments?
箭头函数没有 arguments 对象,但可以用剩余参数收集参数。若必须用 arguments,则需在外层包装普通函数。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。