先记住这个答案
箭头函数不会创建自己的 this 绑定,而是捕获其定义时所处上下文的 this 值作为自己的 this。这被称为词法绑定。因此,使用 call、apply 或 bind 调用箭头函数时,传入的 thisArg 会被忽略,this 仍指向外层作用域的 this。
- 箭头函数无自身 this,定义时继承外层
- call/apply/bind 无法改变箭头函数 this
- 不能用作方法或构造函数
词法继承的具体机制
箭头函数没有独立的 this 绑定,它创建时不会定义自己的 this 环境。当箭头函数体内部出现 this 时,引擎会沿作用域链向外查找,找到最近的非箭头函数的 this 值。若始终未找到,则引用全局对象(严格模式下也如此)。例如普通函数 function f() { return () => this; } 中箭头函数的 this 就是调用 f 时的 this。
正因为如此,call、apply、bind 的第一个参数对箭头函数无效。以 f.call(obj) 为例,传给普通 f 的 obj 会变为 f 内部 this,但 f 返回的箭头函数已捕获了 f 的 this,后续对其调用无法再改。只有使用普通函数表达式才能显式改 this。
function outer() {
return () => this.value;
}
const obj1 = { value: 1 };
const obj2 = { value: 2 };
const arrow = outer.call(obj1);
console.log(arrow()); // 1
console.log(arrow.call(obj2)); // 仍为 1
console.log(arrow.apply(obj2)); // 仍为 1查看输出与解释
1
1
1箭头函数捕获 outer 被调用时的 this(obj1),call/apply 无法改变。
事件回调与类字段的实际场景
在类的方法中,若使用箭头函数定义字段,例如 handleClick = () => { this.count++; },该箭头函数在构造函数中创建,捕获构造时的 this(即实例)。因此即使将方法解构后作为事件回调传入 addEventListener,this 依然指向实例,无需额外 bind。
而用普通方法时,将方法传给事件回调会丢失 this,必须手动 bind 或包裹一层。代价是每次创建实例都会产生新函数,增加内存。选择标准:若频繁通过事件回调传递且需保持实例上下文,可牺牲少量内存使用箭头字段;若方法只需通过实例调用,普通方法更节省资源。
词法绑定的失效条件
箭头函数的 this 永不改变,但若外层作用域是普通函数,且该普通函数被 call 改变 this,那么箭头函数捕获的是改变后的值,因为定义时机也在调用时。例如 function f() { return () => this.x; } f.call({x:1}) 返回箭头函数 this 为 1,若之后 f 以另一种方式调用则生成不同箭头。这不是失效,是捕获时机。
真正的问题是箭头函数不能作为对象方法,因为对象字面量中的 this 是外层作用域,而非对象本身。若必须用方法语法且不依赖动态 this,可用普通函数。此外,在全局定义箭头函数其 this 为全局对象,严格模式也一样,容易意外访问全局属性,应避免。
容易答错的地方
- 箭头函数 this 由调用时最近对象决定
- 错误。箭头函数 this 与调用方式无关,它查找词法作用链,即使作为对象方法调用也继承外层。例如
obj.method = () => this中 this 不是 obj。若需对象方法,用普通函数。 - bind 可以绑定箭头函数 this
- 错误。
bind对箭头函数无效,它返回的新函数仍保留原词法 this。MDN 明确示例显示boundAdd调用结果与未绑定相同。企图用 bind 改变箭头函数是徒劳。
面试官还会怎么问?
箭头函数能通过 new 调用吗?
不能。箭头函数没有 [[Construct]],也没有 prototype 属性,所以用 new 调用会抛 TypeError。若需要构造实例,必须用普通函数或 class。
如何在箭头函数中使用外层函数的 arguments?
箭头函数没有自己的 arguments,但可直接引用外层函数作用域中的 arguments 对象。若箭头函数位于非函数作用域,则引用最近的外层函数参数,例如 function f(){ return ()=>arguments[0]; }。推荐用剩余参数替代更明确。
类字段中箭头函数比 bind 更优吗?
各有利弊。箭头字段每次实例创建新函数,bind 也会创建新函数。但箭头字段写法简洁且自动绑定。若方法不常作为回调传递,普通方法配合 bind 在构造函数中绑定一次也可。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。