JavaScript 中 this 指向问题
一、Bug 场景
在一个 JavaScript 的网页交互项目中,有一个构造函数定义了一个对象,该对象包含一个方法用于更新 DOM 元素的文本内容。同时,为了实现异步操作,在这个方法内部使用了 setTimeout 来模拟一些延迟任务。
二、代码示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF - 8">
<meta name="viewport" content="width=device - width, initial - scale = 1.0">
<title>this指向问题</title>
</head>
<body>
<div id="target"></div>
<script>
function DOMUpdater() {
this.targetElement = document.getElementById('target');
this.updateText = function () {
setTimeout(function () {
this.targetElement.textContent = '更新后的文本';
}, 1000);
};
}
const updater = new DOMUpdater();
updater.updateText();
</script>
</body>
</html>
三、问题描述
-
预期行为:等待 1 秒后,
id为target的div元素的文本内容应更新为 “更新后的文本”。 -
实际行为:在控制台中会报错
Uncaught TypeError: Cannot set property 'textContent' of null。这是因为在setTimeout内部的回调函数中,this的指向发生了变化。在非严格模式下,setTimeout回调函数中的this指向全局对象(在浏览器环境中是window),而不是DOMUpdater实例对象。由于window中没有targetElement属性,所以会导致this.targetElement为null,进而引发错误。
四、解决方案
-
使用箭头函数:箭头函数没有自己的
this,它的this继承自外层作用域,这样就可以保持this指向DOMUpdater实例对象。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF - 8">
<meta name="viewport" content="width=device - width, initial - scale = 1.0">
<title>this指向问题 - 箭头函数解决</title>
</head>
<body>
<div id="target"></div>
<script>
function DOMUpdater() {
this.targetElement = document.getElementById('target');
this.updateText = function () {
setTimeout(() => {
this.targetElement.textContent = '更新后的文本';
}, 1000);
};
}
const updater = new DOMUpdater();
updater.updateText();
</script>
</body>
</html>
-
使用变量保存 this:在
updateText方法内部,使用一个变量(通常命名为self或that)来保存this的值,然后在setTimeout回调函数中使用这个变量。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF - 8">
<meta name="viewport" content="width=device - width, initial - scale = 1.0">
<title>this指向问题 - 变量保存this解决</title>
</head>
<body>
<div id="target"></div>
<script>
function DOMUpdater() {
this.targetElement = document.getElementById('target');
this.updateText = function () {
const self = this;
setTimeout(function () {
self.targetElement.textContent = '更新后的文本';
}, 1000);
};
}
const updater = new DOMUpdater();
updater.updateText();
</script>
</body>
</html>
-
使用
bind方法:bind方法可以创建一个新的函数,在这个新函数中,this被绑定到指定的对象。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF - 8">
<meta name="viewport" content="width=device - width, initial - scale = 1.0">
<title>this指向问题 - bind解决</title>
</head>
<body>
<div id="target"></div>
<script>
function DOMUpdater() {
this.targetElement = document.getElementById('target');
this.updateText = function () {
setTimeout(function () {
this.targetElement.textContent = '更新后的文本';
}.bind(this), 1000);
};
}
const updater = new DOMUpdater();
updater.updateText();
</script>
</body>
</html>