
本文深入探讨了javascript中`this`上下文在方法作为回调函数时丢失的问题。通过分析`navigator.geolocation.getcurrentposition`等场景,详细阐述了为何直接传递方法会导致`this`指向错误,以及如何利用`.bind(this)`方法创建一个永久绑定`this`的新函数,从而确保回调函数能够正确访问其所属对象的属性和方法。理解`.bind(this)`对于编写健壮的javascript代码至关重要。
this上下文的动态性
在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: '© OpenStreetMap 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’)等错误。
.bind(this):绑定上下文的利器
为了解决上述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: '© OpenStreetMap 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。
解决方案:箭头函数(推荐): 箭头函数没有自己的this绑定,它会捕获其定义时所处的外部(词法)作用域的this值。这是现代JavaScript中解决这类问题的推荐方式,如示例代码中所示的(mapE) => { … }。再次使用.bind(this): 也可以对内部的匿名函数再次使用.bind(this)。保存this引用(传统方法): 在ES6之前,常见的做法是在外部作用域中将this保存到一个变量(如const self = this;),然后在内部回调中使用self。
性能考虑:bind()方法会创建一个新函数。如果在一个循环中频繁调用bind(),可能会对性能产生轻微影响,但在大多数应用场景下,这种影响可以忽略不计。对于事件监听器等不频繁创建的场景,其影响微乎其微。
何时使用bind():
当需要将一个对象的方法作为回调函数传递给不控制this上下文的API时(如DOM事件监听器、setTimeout、Promise回调、第三方库的回调等)。当你需要预设函数的部分参数(函数柯里化)时,bind()也可以用于此目的,但通常更推荐使用闭包或专用柯里化工具。当你在类组件中定义事件处理函数,并将其作为props传递给子组件时,也常需要使用bind()来确保this指向组件实例(尤其是在React等框架中)。
以上就是JavaScript中this上下文的深度解析与.bind(this)的应用的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1531547.html
微信扫一扫
支付宝扫一扫