直接回答:this 的指向由函数被调用时的位置决定,和函数定义在哪里没关系。同一段函数体,挂在对象上调用就指向那个对象,单独拎出来调用就指向全局对象或 undefined(严格模式);想固定下来用 bind,而箭头函数根本不用管这件事——它直接沿用外层作用域的 this。

一个让人困惑的例子

1
2
3
4
5
6
7
8
9
10
11
12
const obj = {
bar: 1,
foo: function () {
console.log(this.bar);
}
};

const foo = obj.foo;
const bar = 2;

obj.foo(); // 1
foo(); // 2(非严格模式下是全局对象的 bar)

两个调用的函数体一模一样,结果却不同。原因很简单:this 不是函数自带的东西,而是调用时被临时塞进来的。

可以把对象方法想成"函数存在某个地址上,对象保存了这个地址"。obj.foo 只是把地址复制给了 foo 这个变量,foo() 的调用位置上没有任何对象,所以 this 自然不是 obj。

四种调用形态,四种结果

调用形式 this 指向
foo() 独立调用 非严格模式是全局对象,严格模式是 undefined
obj.foo() 方法调用 obj
foo.call(x) / foo.apply(x) x
new Foo() 构造调用 新建出来的那个实例

记住一句话就够用了:看函数名左边有没有东西,有就是它,没有就是全局或 undefined。

改变指向的三个方法

1
2
3
foo.call(obj, 1, 2);    // 参数逐个传
foo.apply(obj, [1, 2]); // 参数用数组传
const bound = foo.bind(obj); // 返回一个 this 被固定住的新函数

三者的区别只在于"立刻执行还是返回新函数"和"参数怎么传"。实战里 bind 最常用,因为回调场景需要的是一个可以稍后执行、但 this 已经定好的函数。

注意:bind 绑定的 this 不会被后续的 call 覆盖,但被 new 调用时 this 会重新指向新实例——这是少数例外。

回调里为什么会丢 this

1
2
3
setTimeout(obj.foo, 0); // this 丢了
setTimeout(obj.foo.bind(obj), 0); // 正确
setTimeout(() => obj.foo(), 0); // 也可以

把方法当回调传出去,等于只是把函数地址交了出去,调用点变成了定时器内部,this 自然就不对了。React 类组件里"方法里拿不到 this.props"就是同一个原因,解决办法也是这两个:构造函数里 bind,或者用箭头函数定义方法。

箭头函数:不要试图改变它

箭头函数没有自己的 this,它用的是定义时外层作用域的 this,而且 call、apply、bind 都改不了。

1
2
3
4
5
6
const obj = {
bar: 1,
foo: () => {
console.log(this.bar);
}
};

上面这段里 this 是外层(模块作用域或全局)的 this,不是 obj。所以对象方法不要写成箭头函数,而回调、map、then 这类需要沿用外层 this 的地方,箭头函数刚好合适。

顺带一提,箭头函数同样没有自己的 arguments 和 super,需要这两个的时候也不能用。

几种容易判断错的写法

1
2
3
4
5
6
7
8
const obj = {
a: 1,
b: {
a: 2,
foo: function () { return this.a; }
}
};
obj.b.foo(); // 2,指向最近的那个对象 b

this 只看紧挨着点号的最后一个对象,和它在对象里嵌套了多深无关。

1
2
const foo = obj.b.foo;
foo(); // undefined(严格模式);顺手赋个值再调就是那个新对象

链式调用被拆开,this 也就随之消失。

实用结论

  1. 只想记住一条:this 由调用点决定,不由定义点决定;
  2. 需要固定 this 时优先用箭头函数定义外层函数,或者显式 bind;
  3. 对象方法用简写或普通函数,回调用箭头函数,这个默认选择能避开大部分坑;
  4. 类组件里的事件处理函数记得 bind,或者改用函数组件和 Hooks——Hooks 里压根没有 this 这事。

本文整理自我自己早先的 JS 笔记,参照了阮一峰《JavaScript 的 this 原理》的思路,用自己的例子重新写了一遍,文字由 AI 协助改写后经我复核。

这篇笔记整理自我自己的实践记录,如果做法有出入,或者你踩过别的坑,欢迎到留言板一起聊聊。

站内搜索

没有找到内容!