如何在回调函数中访问正确的 `this`

如何在回调函数中访问正确的 this

技术背景

在 JavaScript 里,this 关键字的表现与其他语言有所不同,并且在严格模式和非严格模式下也存在差异。其值主要由函数的调用方式决定,无法在执行期间通过赋值来设定,每次调用函数时 this 的值都可能不同。在回调函数中,this 的指向常常不符合预期,所以我们需要掌握一些方法来确保能访问到正确的 this

实现步骤

了解 this 的基本规则

this(即“上下文”)是每个函数内部的特殊关键字,其值仅取决于函数的调用方式,而非定义方式、时间或位置。以下是一些示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
function foo() {
console.log(this);
}

// 普通函数调用
foo(); // `this` 指向 `window`

// 作为对象方法
var obj = {bar: foo};
obj.bar(); // `this` 指向 `obj`

// 作为构造函数
new foo(); // `this` 指向从 `foo.prototype` 继承的对象

访问正确的 this 的方法

使用箭头函数

ES6 引入了箭头函数,它可被视为 lambda 函数,没有自己的 this 绑定。this 会像普通变量一样在作用域中查找,因此无需调用 .bind

1
2
3
4
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}

不使用 this

可以创建一个新变量来引用 this 所指向的对象,常见的变量名有 selfthat

1
2
3
4
5
6
7
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}

显式设置回调函数的 this(方法一)

每个函数都有 .bind 方法,它会返回一个新函数,该函数的 this 被绑定到指定的值。

1
2
3
4
5
6
7
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() {
alert(this.data);
}).bind(this);
transport.on('data', boundFunction);
}

在 jQuery 中,可使用 jQuery.proxy 代替 .bind

显式设置回调函数的 this(方法二)

有些接受回调函数的函数或方法也允许传入一个值,使回调函数的 this 指向该值,例如 Array#map

1
2
3
4
5
6
var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj);

处理对象方法作为回调/事件处理程序的问题

当对象方法作为回调/事件处理程序时,this 的指向可能不符合预期。解决方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Foo() {
this.data = 42;
// 使用 .bind
document.body.onclick = this.method.bind(this);

// 或使用匿名函数
var self = this;
document.body.onclick = function() {
self.method();
};

// 或使用箭头函数
document.body.onclick = () => this.method();
}

Foo.prototype.method = function() {
console.log(this.data);
};

核心代码

以下是一个综合示例,展示了多种访问正确 this 的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function Person(name) {
this.name = name;

this.sayNameVersion1 = function(callback) {
callback.bind(this)();
};
this.sayNameVersion2 = function(callback) {
callback();
};
this.sayNameVersion3 = function(callback) {
callback.call(this);
};
this.sayNameVersion4 = function(callback) {
callback.apply(this);
};
}

function niceCallback() {
var parentObject = this;
console.log(parentObject);
}

var p1 = new Person('zami');
p1.sayNameVersion1(niceCallback);
p1.sayNameVersion2(niceCallback.bind(p1));
p1.sayNameVersion3(niceCallback);
p1.sayNameVersion4(niceCallback);

最佳实践

  • 优先使用箭头函数:箭头函数没有自己的 this 绑定,能保留外部作用域的 this 值,语法简洁,适用于大多数回调函数场景。
  • 避免过度使用 this:如果不是必需,尽量不使用 this 和类。对于单例模式的模块,可直接在文件根目录定义变量和函数,然后导出使用。
  • 正确使用 .bind.call.apply:在需要显式设置 this 值时使用这些方法,但要注意避免过度使用,以免使代码变得复杂。

常见问题

setTimeoutthis 的问题

setTimeout 总是以全局对象(Window)执行回调函数,可使用 .bind 解决:

1
2
3
setTimeout(function() {
this.methodName();
}.bind(this), 2000);

箭头函数的限制

箭头函数不适合作为对象方法,也不能用作构造函数,因为它没有自己的 thisargumentssupernew.target 绑定。

类中 this 的问题

在类中使用回调函数时,可使用类字段和箭头函数来确保 this 的正确指向:

1
2
3
4
5
6
7
8
9
class someView {
onSomeInputKeyUp = (event) => {
console.log(this);
};

someInitMethod() {
someInput.addEventListener('input', this.onSomeInputKeyUp);
}
}

不过,这是一个 Stage 3 提案,目前需要使用 Babel 和相应的插件进行处理。


如何在回调函数中访问正确的 `this`
https://119291.xyz/posts/how-to-access-correct-this-inside-callback/
作者
ww
发布于
2025年5月26日
许可协议