先记住这个答案
箭头函数没有自己的 arguments 绑定,这是它与普通函数的重要区别之一。当在箭头函数内部写 arguments 时,JavaScript 引擎会沿作用域链向上查找,找到最近的外层普通函数的 arguments 对象。若定义在顶层(无任何外层普通函数),则 arguments 不可用,访问会抛出 ReferenceError。要获取外层函数的 arguments,直接引用外层函数作用域中的 arguments 即可,但更推荐使用剩余参数语法,因其明确且不受层级干扰。
- 箭头函数无 arguments 绑定,按词法查找外层。
- 可用外层函数作用域中的 arguments 对象。
- 剩余参数是推荐替代,避免歧义。
机制:词法作用域如何影响 arguments
普通函数在每次调用时会创建一个新的 arguments 对象,并将实参按索引存入其中。箭头函数则完全没有这个步骤,其函数体中的 arguments 标识符既不是局部变量也不是参数,而是被视为一个自由变量,解析时沿作用域链向外查找。
当箭头函数定义在某个函数内部时,外层函数的 arguments 对象会出现在其作用域链上,因此箭头函数中的 arguments 直接指向外层函数的 arguments。这并非复制或传递,而是同一对象的引用。若箭头函数位于全局顶层,则不存在任何 arguments 绑定,访问会抛出 ReferenceError。
function outer(a, b) {
const arrow = () => {
console.log(arguments[0], arguments[1]);
};
arrow();
return arguments.length;
}
console.log(outer('x', 'y'));查看输出与解释
x y
2箭头函数 arrow 内访问 arguments 相当于访问 outer 函数的 arguments 对象,因此输出 x y,并最终返回长度 2。
场景:回调中保留外层实参
假设有一个遍历函数 iterate,它接收任意参数,并在异步回调中全部打印。若用普通函数包裹 setTimeout,内层回调需要访问 iterate 的实参。若不使用箭头函数,可能写成 var self = this; var args = arguments; 等。使用箭头函数可以直接引用 arguments,但必须保证箭头函数定义在 iterate 内部。
实际执行时,iterate(1, 2) 会进入函数,内部 setTimeout 的回调是箭头函数,它捕获了外层 arguments。1 秒后输出 [1, 2]。改为剩余参数可避免这种隐式依赖,见下例,使函数更通用。
function demo() {
setTimeout(() => {
console.log(JSON.stringify(Array.from(arguments)));
}, 0);
}
demo('a', 'b');
function demo2(...args) {
setTimeout(() => {
console.log(JSON.stringify(args));
}, 0);
}
demo2(1, 2);查看输出与解释
["a","b"]
[1,2]第一个 demo 的回调引用外层 arguments,得到实参数组;第二个用剩余参数对象 args,效果相同但意图明确,且可避免某些便捷方法的误用。
边界:何时无法取到外层 arguments
若箭头函数不在任何普通函数体内,而是位于模块顶层或脚本全局,则 arguments 标识符不存在,访问即抛 ReferenceError。另外,即便在函数内,若该函数本身也是箭头函数,则需继续向外查找,直到普通函数或全局,容易造成混乱。
在严格模式下,普通函数内部 arguments 仍是可用的,但 arguments.callee 被禁用,因此不能通过 callee 递归。替代方案包括命名函数表达式或剩余参数。此外,arguments 对象按实际传入的实参构造,当使用默认参数或剩余参数时,它既不会体现默认参数的填充值,也不与剩余参数的数组对应,因此与形参列表可能不一致,增加出错风险。
容易答错的地方
- 误以为箭头函数继承外层 this 也继承 arguments
- 箭头函数词法绑定 this 和 arguments 是独立的。arguments 同样按词法查找,但若内层有普通函数包裹,它会指向那个函数的 arguments,未必是最外层。
- 认为箭头函数可以用 call 传入 arguments
- 调用箭头函数用 call/apply 传参时,函数体内不会创建 arguments,传入的实参只能通过具名参数或剩余参数访问。箭头函数自身的 arguments 不受调用影响,仍是闭合词法作用域中的值。
面试官还会怎么问?
箭头函数内想要拿到自己的实参怎么办?
不能访问 arguments,应使用剩余参数。例如 (...args) => args。它提供真正的数组,并可安全传递。
外层普通函数直接 return 箭头函数,内部 arguments 会失效吗?
只要 return 的箭头函数定义在该普通函数体内,其 arguments 引用就始终合法,即使外层执行完毕后仍可通过闭包访问,不受调用栈影响。
在对象方法中定义箭头函数,arguments 指向谁?
若该对象方法是普通函数,则指向该方法的 arguments;若对象方法本身也是箭头函数或对象里用箭头函数定义属性,则继续向上查找;若外层无普通函数(如模块或全局顶层),则 arguments 不可用,访问会抛出 ReferenceError。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。