先记住这个答案
普通函数每次被调用,都会新建一个 arguments 对象;箭头函数不建。所以在箭头函数里写 arguments,引擎把它当成普通变量,沿着作用域链往外找,找到的是包着它的最近一个普通函数的 arguments。外面没有普通函数,比如在 ES 模块顶层,就会抛 ReferenceError。想拿箭头函数自己这次调用的参数,用 rest 参数 (...args) => {}。
- 箭头函数里的
arguments属于定义它的外层普通函数 - 往外找看的是写在哪,不是谁调用了它
- 外面没有普通函数时会抛
ReferenceError - 要拿箭头自己的参数,用 rest 参数
两层参数放在一起看
外层普通函数收到两个参数,里面的箭头函数收到三个参数,分别打印出来就清楚了。
arguments 和 rest 的来源不同JavaScriptfunction outer(a, b) {
const inner = (...own) => {
console.log(arguments.length, arguments[0]) // outer 的参数
console.log(own.length, own[0]) // inner 自己的参数
}
inner('x', 'y', 'z')
}
outer('a', 'b')查看输出与解释
2 a
3 xinner 明明是用三个参数调用的,里面的 arguments 却只有两个元素,第一个是 'a'。因为 arguments 是 outer 那次调用的,own 才是 inner 自己的。
这和闭包读外层变量是同一回事。箭头函数写在 outer 里,它能看到 outer 的局部变量,arguments 也被当成 outer 的一个局部变量。查找只看代码写在哪,不看运行时谁调用了它。
为什么这样设计:回调里想要外层的东西
箭头函数是为短回调设计的。写回调时,大多数情况想用的其实是外层函数的 this 和参数,而不是回调自己的。以前的写法是先 var self = this; var args = arguments,再在回调里用,箭头函数把这一步省掉了。
argumentsJavaScriptfunction debounce(fn, wait) {
let timer
return function () {
clearTimeout(timer)
// 箭头函数里的 this 和 arguments 都是外层 function 这次调用的
timer = setTimeout(() => fn.apply(this, arguments), wait)
}
}
const log = debounce((a, b) => console.log(a, b), 100)
log(1, 2)
log(3, 4) // 前一次被清掉,只执行这一次查看输出与解释
3 4setTimeout 调用箭头函数时不会传参数,但箭头函数里的 arguments 本来就不是它自己的,读到的是返回的那个普通函数最后一次被调用时的参数。如果这里写成普通 function,arguments 就变成定时器回调自己的,是空的。
- 什么时候不能用箭头函数
- 函数需要自己的
this、需要自己的arguments、需要被new调用,这三种情况都要用普通函数。如果只是需要不定数量的参数,箭头函数加 rest 就够,不必为了arguments改回普通函数。
容易答错的地方
- 以为箭头函数会拿到调用者的
arguments - 作用域是按代码写的位置确定的,不会顺着调用栈去找。把箭头函数传给别的函数去调用,它读到的仍是定义时外层函数的
arguments。 - 在模块顶层读
arguments - ES 模块和浏览器的普通脚本顶层都没有
arguments,会抛ReferenceError。Node.js 的 CommonJS 文件顶层能读到,是因为模块代码被包在一个函数里执行,读到的是那个包装函数的参数,和你的业务没有关系。
面试官还会怎么问?
箭头函数的 this 是不是也这样?
是一样的。箭头函数不建自己的 this,读的是外层的。所以对箭头函数调用 call、apply、bind 传进去的 this 都会被忽略,只有参数能传进去。
rest 参数和 arguments 有什么区别?
rest 参数是真数组,可以直接 map、filter;它只收集没有被前面形参接住的那部分;箭头函数里也能用。arguments 是类数组,收集全部实参,箭头函数里没有自己的。
箭头函数一直引用外层 arguments,会不会占内存?
只要箭头函数还被引用着,比如挂在事件监听或定时器上,外层函数的作用域就不会被回收,里面的 arguments 也一起留着。长期存在的回调只拿需要的那几个值,用完及时解绑。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。