
本文深入探讨了javascript中`this`上下文在方法作为回调函数时丢失的问题。通过分析`navigator.geolocation.getcurrentposition`等场景,详细阐述了为何直接传递方法会导致`this`指向错误,以及如何利用`.bind(this)`方法创建一个永久绑定`this`的新函数,从而确保回调函数能够正确访问其所属对象的属性和方法。理解`.bind(this)`对于编写健壮的javascript代码至关重要。
在JavaScript中,this关键字的指向并非固定不变,而是取决于函数被调用的方式。它在不同执行上下文中的行为是JavaScript初学者常遇到的一个难点。通常,this指向调用该函数的对象。然而,当一个对象的方法被提取出来作为独立函数或者作为回调函数传递给其他API时,其原有的this上下文关系就会被打破。
考虑以下场景,一个类中的方法需要作为回调函数传递:
class App {
#map; // 私有字段,用于存储地图实例
#mapEvent; // 私有字段,用于存储地图事件对象
constructor() {
this._getPosition();
}
_getPosition() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
this._loadMap, // 问题所在:_loadMap 方法被作为普通函数传递,'this'上下文将丢失
() => {
alert("could not get your position");
}
);
}
}
_loadMap(position) {
const { latitude, longitude } = position.coords;
const coords = [latitude, longitude];
// 期望 'this' 指向 App 实例,以便访问 #map 属性
this.#map = L.map('map').setView(coords, 13);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(this.#map);
L.marker(coords)
.openPopup()
.addTo(this.#map) // 注意这里是 this.#map
.bindPopup('You are here!');
// 嵌套回调函数,'this' 再次成为潜在问题
this.#map.on("click", function(mapE) {
// 这里期望 'this' 指向 App 实例,但实际上会指向事件源(地图对象)或全局对象(非严格模式下)
// 在严格模式下,'this' 为 undefined,导致错误
this.#mapEvent = mapE;
// form.classList.remove("hidden");
// inputDistance.focus();
});
}
}在上述_getPosition方法中,navigator.geolocation.getCurrentPosition是一个浏览器API,它期望接收一个成功回调函数。当我们将this._loadMap直接传递给它时,_loadMap方法实际上是作为getCurrentPosition的参数被独立调用。在这种调用方式下,_loadMap内部的this将不再指向App类的实例。在严格模式下(ES6模块和类默认启用严格模式),this会是undefined,导致尝试访问this.#map时抛出TypeError: Cannot set properties of undefined (setting '#map')等错误。
为了解决上述this上下文丢失的问题,JavaScript提供了Function.prototype.bind()方法。bind()方法会创建一个新函数,这个新函数在被调用时,其this关键字会被永久性地设置为bind()的第一个参数。
立即学习“Java免费学习笔记(深入)”;
让我们看看如何使用.bind(this)来修正_getPosition中的问题:
class App {
#map;
#mapEvent;
constructor() {
this._getPosition();
}
_getPosition() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
this._loadMap.bind(this), // 关键:使用 .bind(this) 绑定当前 App 实例的上下文
() => {
alert("could not get your position");
}
);
}
}
_loadMap(position) {
const { latitude, longitude } = position.coords;
const coords = [latitude, longitude];
this.#map = L.map('map').setView(coords, 13);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(this.#map);
L.marker(coords)
.openPopup()
.addTo(this.#map)
.bindPopup('You are here!');
// 改进:使用箭头函数或再次使用 .bind(this) 确保内部回调的 this
this.#map.on("click", (mapE) => { // 使用箭头函数,'this' 继承自外部作用域(App 实例)
this.#mapEvent = mapE;
// form.classList.remove("hidden");
// inputDistance.focus();
});
// 或者,如果不用箭头函数:
// this.#map.on("click", function(mapE) {
// this.#mapEvent = mapE;
// }.bind(this)); // 再次使用 .bind(this)
}
}this._loadMap.bind(this)中this的含义:
这里的this指的是在调用_getPosition方法时,_getPosition所处的上下文中的this。由于constructor方法调用了this._getPosition(),所以_getPosition内部的this正确地指向了App类的一个实例。因此,当执行到this._loadMap.bind(this)时,bind()方法接收到的this参数,正是当前的App实例。
bind()方法会返回一个新的函数。这个新函数在未来无论何时被调用(例如,当getCurrentPosition成功获取位置信息后调用它),其内部的this都将永久指向创建它时传递给bind()的那个App实例。这样,_loadMap内部的this.#map就能正确地引用到App实例上的#map私有属性。
嵌套回调中的this问题: 如_loadMap方法中this.#map.on("click", function(mapE) { ... })所示,即使外部的_loadMap已经通过.bind(this)绑定了this,其内部的匿名函数回调仍然会面临this上下文丢失的问题。在这种情况下,this通常会指向触发事件的元素(如果事件处理函数被直接作为DOM事件处理程序),或者在严格模式下为undefined。
性能考虑:bind()方法会创建一个新函数。如果在一个循环中频繁调用bind(),可能会对性能产生轻微影响,但在大多数应用场景下,这种影响可以忽略不计。对于事件监听器等不频繁创建的场景,其影响微乎其微。
何时使用bind():
以上就是JavaScript中this上下文的深度解析与.bind(this)的应用的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号