官方: The this keyword refers to the context where a piece of code, such as a function’s body, is supposed to run. Most typically, it is used in object methods, where this refers to the object that the method is attached to, thus allowing the same method to be reused on different objects.

The value of this in JavaScript depends on how a function is invoked (runtime binding), not how it is defined. When a regular function is invoked as a method of an object (obj.method()), this points to that object. When invoked as a standalone function (not attached to an object: func()), this typically refers to the global object (in non-strict mode) or undefined (in strict mode). The Function.prototype.bind() method can create a function whose this binding doesn’t change, and methods Function.prototype.apply() and Function.prototype.call() can also set the this value for a particular call.

Arrow functions differ in their handling of this: they inherit this from the parent scope at the time they are defined. This behavior makes arrow functions particularly useful for callbacks and preserving context. However, arrow functions do not have their own this binding. Therefore, their this value cannot be set by bind(), apply() or call() methods, nor does it point to the current object in object methods.

概要

this 是 JavaScript 中的一个关键概念,它表示函数执行时的执行上下文。不同于很多语言,JavaScript 中 this 的指向在函数调用时确定,而不是在定义时决定。理解 this 的规则能帮助你避免许多常见错误(尤其是在事件回调、类方法、异步回调中)。


快速结论(口诀)

调用者决定 this,箭头函数继承外层 this,new 创建新对象并作为 this,call/apply/bind 显式指定。优先级:bind(显式,上下文固定) > new > call/apply > 隐式(对象方法) > 默认(全局或 undefined)。


一、this 的五条基本规则(从高到低优先级)

  1. 显式绑定(bind 优先)

    • const fn2 = fn.bind(obj) 会返回一个永久绑定 obj 的新函数;之后无论如何调用,this 都是 obj
  2. 构造函数调用(new

    • 使用 new Fn() 时,this 指向新创建的实例对象(除非构造函数显式返回对象)。
  3. 显式调用(call / apply

    • fn.call(obj, ...)fn.apply(obj, args) 立即调用函数并把 this 设为 obj
  4. 隐式绑定(方法调用)

    • obj.fn()this 指向调用点左侧的对象 obj(也称为“调用者”)。注意:如果链式访问或临时值则可能丢失。
  5. 默认绑定

    • 普通函数直接调用(非严格模式下 this 为全局对象 window;严格模式下为 undefined)。

注:箭头函数不按照以上规则,它不绑定 this,而是词法继承外层作用域的 this(即创建时上下文的 this)。


二、优先级与典型例子

1) 隐式绑定(对象方法)

const obj = {
  name: 'Alice',
  say() {
    console.log(this.name);
  }
};
obj.say(); // 'Alice' —— this = obj

2) 默认绑定

function foo() {
  console.log(this);
}
foo(); // 非严格模式:window;严格模式:undefined

3) 显式绑定(call / apply / bind)

function greet(g) {
  console.log(g + ', ' + this.name);
}
const user = { name: 'Bob' };

greet.call(user, 'Hello');        // Hello, Bob
greet.apply(user, ['Hi']);        // Hi, Bob

const bound = greet.bind(user);
bound('Hey');                     // Hey, Bob

4) new 绑定

function Person(name) {
  this.name = name;
}
const p = new Person('Tom');
console.log(p.name); // Tom

5) 箭头函数(词法 this)

const obj = {
  name: 'Lily',
  method: function() {
    const arrow = () => {
      console.log(this.name);
    };
    arrow(); // 'Lily',箭头函数继承 method 的 this
  }
};
obj.method();

三、call / apply / bind 的深入对比(何时用哪个)

  • call(obj, arg1, arg2, …):立即调用,参数逐个传。
  • apply(obj, [arg1, arg2, …]):立即调用,参数以数组形式传。
  • bind(obj, …presetArgs):不立即调用,返回一个新函数,永久绑定 this,可作事件回调、延迟调用用。

示例 — 事件绑定与回调场景

class Button {
  constructor(name, el) {
    this.name = name;
    this.el = el;
    // 如果不 bind,callback 的 this 指向的是 DOM 节点而不是实例
    this.el.addEventListener('click', this.onClick.bind(this));
  }
  onClick() {
    console.log(this.name);
  }
}

四、事件回调里的 this(浏览器行为)

  • 在浏览器环境,使用 addEventListener 注册事件时,事件处理函数内部的 this 默认是 event.currentTarget(即事件监听被附加的对象),除非你用 bind、箭头函数或其他技巧改变它。
  • 官方参考:MDN 对 addEventListenerthis 的说明(文末参考链接)。

五、常见陷阱与误区

  1. 方法作为回调丢失 this

    const obj = { name: 'A', fn() { console.log(this.name); } };
    setTimeout(obj.fn, 100); // this 丢失,默认绑定或 undefined(严格模式)
    // 解决:setTimeout(obj.fn.bind(obj), 100) 或 setTimeout(() => obj.fn(), 100)
    
  2. 链式访问导致 this 丢失

    const x = { a: { fn() { console.log(this); } } };
    const f = x.a.fn;
    f(); // 默认绑定,this 不是 x.a
    
  3. 箭头函数不是万能解决方案

    • 在类方法中使用箭头函数可以避免 bind,但会在每次实例创建时重新分配函数,影响内存(如果大量实例需注意)。
    • 箭头函数也不能作为构造函数(不能用 new)。
  4. bind 的参数优先级

    • bind 固定 this,即使随后用 call/apply 也无法改变(绑定优先级高)。
  5. 在 React 中,类组件需要绑定事件处理

    • 常见做法:this.method = this.method.bind(this) 或使用类字段语法 method = () => {}

六、调试与检查 this

  • 在 Chrome 控制台里,可以直接在函数内 console.log(this)
  • 或使用 console.log(this === expectedObj) 来断言。
  • 在事件回调中,优先检查 event.target(触发元素)与 event.currentTarget(绑定目标)的区别:this 等同于 event.currentTarget(非委托场景)。

七、工程实战建议(Best Practices)

  1. 明确边界:组件/类内的方法避免直接传裸函数作为回调;使用 bind、箭头函数或包装函数。
  2. prefer explicit binding:在构造函数中用 this.method = this.method.bind(this),或在使用地方用 fn.bind(this)
  3. 使用箭头函数处理闭包中的 this 问题:在内部回调里使用箭头函数继承 this
  4. 避免滥用全局 this:开启严格模式('use strict')可避免非预期的全局 this。
  5. 在类/组件中尽量使用类字段(class fields)语法:可以在声明时用箭头函数保持 this,但要权衡性能。
  6. 写单元测试时显式传入 this:测试回调时用 call/apply 显式指定上下文,增加可读性。

八、TypeScript 下的 this 小提示

  • TypeScript 支持在方法签名中声明 this 的类型:method(this: MyClass, arg: number) {},可以在编译期检查 this 的使用。
  • 推荐在类中使用 public method = () => {} 或在 constructor 中绑定,避免 this 未按预期指向的问题。

九、参考资料


十、示例集合(可直接复制运行)

请在浏览器控制台或Node环境(注意严格模式差异)运行下列示例理解 this

<button id="b1">btn</button>
<script>
  // 隐式绑定
  const o = { v: 1, show() { console.log(this.v); } };
  o.show(); // 1

  // 丢失绑定
  const f = o.show;
  f(); // undefined 或 window.v

  // 立即绑定
  const bound = o.show.bind(o);
  setTimeout(bound, 0); // 1

  // 箭头继承 this
  const o2 = {
    v: 2,
    method() {
      const arrow = () => console.log(this.v);
      arrow(); // 2
    }
  };
  o2.method();

  // 事件中的 this
  document.getElementById('b1').addEventListener('click', function (e) {
    console.log(this === e.currentTarget); // true
  });
</script>

十一、总结(再次强调)

  • 理解 this 的核心在于调用方式,不是函数定义的位置。
  • 箭头函数、bindcallapplynew 都会改变或固定 this 的行为,了解优先级能帮助你写出更可靠的代码。
  • 工程上优先使用显式绑定或箭头函数以避免回调时 this 丢失,同时注意性能与内存开销。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐