DrillLab
第 37 / 105 道37 / 105 · #303

this 指向什么

What does 'this' refer to

先自己答,再往下看Answer it yourself first

一句话:this 是调用时决定的,不是定义时。看「谁调用的」。

四条规则,按优先级从高到低—— 这个顺序是标准答案:

  1. new 绑定——new Foo(),this 是新创建的对象。
  2. 显式绑定——call / apply /bind 指定的那个。
  3. 隐式绑定——obj.fn(),this 是 obj(看点号左边)。
  4. 默认绑定—— 都不满足时,严格模式下是 undefined,否则是 window / global。

箭头函数是例外,它不参与这四条—— 它没有自己的 this,用的是定义时外层作用域的 this,而且 call / bind改不了它。

最经典的陷阱:隐式丢失。const fn = obj.method 之后单独调fn(),点号没了,this 就丢了。把方法当回调传出去(setTimeout(obj.method)、onClick={this.handle})都是这个问题 ——这就是 React 类组件必须在构造器里bind 的原因。

会追问:「DOM 事件回调里 this 是什么?」—— 普通函数是绑定事件的那个元素(等于 e.currentTarget),箭头函数则是外层的 this。

In one line: this is decided when the function is called, not where it was written. Look at who called it.

Four rules, highest priority first — this order is the model answer:

  1. new binding — with new Foo(), this is the object that was just created.
  2. Explicit binding — whatever you handed to call / apply / bind.
  3. Implicit binding — obj.fn() makes this the obj (look left of the dot).
  4. Default binding — when none of the above applies: undefined in strict mode, otherwise window / global.

Arrow functions are the exception; they do not play by those four rules — an arrow has no this of its own, it uses the this of the scope it was defined in, and call / bind cannot change that.

The classic trap: the implicit binding gets lost. Do const fn = obj.method and then call fn() on its own — the dot is gone, so this is gone. Handing a method off as a callback (setTimeout(obj.method), onClick={this.handle}) is the same bug — and it is exactly why React class components had to bind in the constructor.

Follow-up: “What is this inside a DOM event handler?” — in a normal function it is the element the listener is attached to (the same as e.currentTarget); in an arrow function it is the outer this.

JavaScript四条规则与隐式丢失The four rules, and losing the implicit binding示意Illustrative
1const obj = {
2 label: "obj",
3 show() { console.log(this.label); },
4};
5
6obj.show(); // "obj" 隐式绑定,看点号左边
7const f = obj.show;
8f(); // 不是 "obj":严格模式(模块、class)下 this 是 undefined,直接抛 TypeError;非严格时是全局对象,打印 undefined
9f.call({ label: "call" }); // "call" 显式绑定
10setTimeout(obj.show, 0); // undefined 传出去就丢了
11setTimeout(() => obj.show(), 0); // "obj" 包一层就保住了
12
13// 箭头函数不参与规则,call 也改不了
14const arrow = () => console.log(this);
15arrow.call({ a: 1 }); // 还是外层的 this
16
17// React 类组件为什么要 bind
18class Btn extends React.Component {
19 constructor(p) {
20 super(p);
21 this.handle = this.handle.bind(this); // 不 bind,onClick 里 this 就是 undefined
22 }
23 handle() { console.log(this.props); }
24}
1const obj = {
2 label: "obj",
3 show() { console.log(this.label); },
4};
5
6obj.show(); // "obj" implicit binding: look left of the dot
7const f = obj.show;
8f(); // not "obj": in strict mode (modules, classes) this is undefined and it throws a TypeError; in sloppy mode this is the global object and it prints undefined
9f.call({ label: "call" }); // "call" explicit binding
10setTimeout(obj.show, 0); // undefined pass it out and the binding is gone
11setTimeout(() => obj.show(), 0); // "obj" one wrapper keeps it
12
13// An arrow function ignores these rules, and call cannot change it either
14const arrow = () => console.log(this);
15arrow.call({ a: 1 }); // still the outer this
16
17// Why a React class component needs bind
18class Btn extends React.Component {
19 constructor(p) {
20 super(p);
21 this.handle = this.handle.bind(this); // without bind, this is undefined in onClick
22 }
23 handle() { console.log(this.props); }
24}