先记住这个答案
规则:默认绑定(独立调用指向全局或 undefined);隐式绑定(obj.fn() 指向 obj);显式绑定(call/apply/bind 指定 this);new 绑定(构造调用新建对象)。优先级:new 最高,显式绑定其次,隐式绑定再其次,默认最低。若同时出现,先按规则筛选,再按优先级裁决。
- 默认绑定最低,
this为全局或undefined - 隐式绑定看调用对象,不等于定义处
new优先级最高,且显式高于隐式
四条规则的触发条件与优先级依据
默认绑定指函数无任何限定直接调用,如 fn()。非严格模式 this 指向 globalThis(浏览器为 window);严格模式为 undefined。这是兜底规则,优先级最低。
隐式绑定要求调用形式为 obj.method(),此时函数体 this 指向 obj。但实际绑定的对象是调用发生时被obj引用的对象,与函数声明所在对象无关。若只取方法再单独调用,则丢失绑定。
显式绑定通过 call(that, ...args) 和 apply(that, argsArray) 强制指定 this,而 bind 创建新函数,其 this 永久固定。显式绑定优先级高于隐式,原因是调用点明确传入 this。
new 绑定发生在用 new 调用函数时,JavaScript 会新建对象并让函数内 this 指向该对象,即使函数体有 call 改写也无济于事——new 是语言层面的最高干预。
对象方法名作为回调时的 this 丢失
假设一个 counter 对象,方法 increment 内部依赖 this.count。setTimeout(counter.increment, 1000) 会把函数引用传出,调用点是定时器,无对象前缀,触发默认绑定。严格模式下 this 为 undefined,触发 TypeError。
解决需显式绑定:setTimeout(counter.increment.bind(counter), 1000)。因为 bind 生成的函数无论后续怎么传参或作为独立函数调用,其 this 永远是 counter,保证回调执行正确。
另一个场景是数组遍历:[1,2].forEach(counter.increment) 内部实现会把回调作为独立的函数调用,同样丢失。应当写成 forEach(counter.increment, counter) 或使用箭头函数保留词法作用域,但箭头函数本主题不展开。
规则在严格模式与绑定函数中的边界
默认绑定受严格模式影响:若函数体处于 "use strict",独立调用 this 为 undefined,而不是全局对象。这与调用处是否严格无关,而是看函数定义时的模式。
bind 创建的绑定函数不能被 call 或 apply 再次更改 this,即使强行调用也保持初始值。但注意 bind 本身不会阻止 new 调用,以 new 调用绑定函数时,new 绑定会覆盖原 this,这是极少数例外。
隐式绑定在严格模式下若访问 obj.method 并直接调用,仍遵循默认绑定规则,但若方法本身是访问器属性,则与原型链相关,需单独分析。
容易答错的地方
- define-time 绑定误解
- 常见认为
this在函数定义时绑定到所在对象。实际上this是调用期决定,同一函数赋给不同对象,调用结果不同。例如const a = obj.method; a()与非严格模式会指向全局,而非obj。 - bind 与 call 同时用混淆
- 误以为
fn.bind(ctx).call(other)中other会覆盖ctx。事实是bind返回新函数,其this永久固定为ctx,后续call无效,除非用new调用才可能改变。
面试官还会怎么问?
new 绑定和显式绑定同时存在时,谁优先?
new 优先。例如 const obj = new (fn.bind(that))(),生成的新对象覆盖 bind 提供的 that。new 构造时函数内部 this 指向新对象,bind 只影响普通调用。
隐式绑定丢失后如何恢复?
使用 Function.prototype.bind 将方法绑定到对象上形成新函数,或利用箭头函数捕获外层 this(但不在本题)。另一种方式是在调用处保留对象引用:setTimeout(() => obj.method()),该箭头函数内部 obj.method() 为隐式绑定。
严格模式对默认绑定和隐式绑定是否有影响?
严格模式只改变默认绑定的值:全局变为 undefined。隐式绑定和显式绑定、new 绑定行为不变,仅函数体内的 this 值按规则设置,与是否严格无关。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。