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()thisobj看点号左边)。
  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 bindingobj.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 name: "obj",
3 show() { console.log(this.name); },
4};
5
6obj.show(); // "obj" 隐式绑定,看点号左边
7const f = obj.show;
8f(); // undefined 隐式丢失
9f.call({ name: "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 name: "obj",
3 show() { console.log(this.name); },
4};
5
6obj.show(); // "obj" implicit binding: look left of the dot
7const f = obj.show;
8f(); // undefined the implicit binding is lost
9f.call({ name: "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}