前文已阐述JavaScript this
关键字的基础用法。this
指向的关键在于运行时上下文。然而,上下文在预期之外发生变化时,问题就出现了。本文将重点介绍这种情况,以及如何解决。
核心要点
this
关键字指向当前执行上下文,理解它对于操作和交互对象至关重要,尤其是在面向对象编程或使用大量依赖this
的框架和库时。this
关键字的常见问题包括在提取的方法、回调函数和闭包中使用。这些问题可以通过使用bind()
方法将this
关键字显式绑定到正确的对象来解决。this
值。箭头函数的词法绑定无法被覆盖,使其成为维护this
正确上下文更优雅的解决方案。this
的值取决于函数的调用方式。在方法中,this
指向它所属的对象;在普通函数中,this
指向全局对象;如果用new
关键字(作为构造函数)调用函数,this
指向新创建的对象;在事件处理程序中,this
指向接收事件的元素;最后,可以使用call()
、apply()
或bind()
显式设置this
。解决常见问题
本节将探讨使用this
关键字时出现的一些最常见问题,并学习如何解决它们。
1. 在提取的方法中使用this
最常见的错误之一是尝试将对象的方法赋值给变量,并期望this
仍然指向原始对象。如下例所示,这行不通。
var car = { brand: "Nissan", getBrand: function() { console.log(this.brand); } }; var getCarBrand = car.getBrand; getCarBrand(); // 输出:undefined
即使getCarBrand
似乎是car.getBrand()
的引用,实际上它只是getBrand()
自身的另一个引用。我们已经知道,调用位置决定上下文,此处调用位置是getCarBrand()
,这是一个简单的函数调用。为了证明getCarBrand
指向一个无基础的函数(未绑定到任何特定对象的函数),只需在代码底部添加alert(getCarBrand);
,您将看到以下输出:
var car = { brand: "Nissan", getBrand: function() { console.log(this.brand); } }; var getCarBrand = car.getBrand; getCarBrand(); // 输出:undefined
getCarBrand
只包含一个普通函数,它不再是car
对象的成员方法。因此,在这种情况下,this.brand
实际上转换为window.brand
,当然它是未定义的。如果我们从对象中提取方法,它又变成了一个普通函数。它与对象的连接被切断,不再按预期工作。换句话说,提取的函数没有绑定到它所取自的对象。那么我们如何补救呢?如果我们想保留对原始对象的引用,我们需要在将getBrand()
函数赋值给getCarBrand
变量时,显式地将getBrand()
函数绑定到car
对象。我们可以使用bind()
方法来实现。
function(){ console.log(this.brand); }
现在,我们得到了正确的输出,因为我们成功地将上下文重新定义为我们想要的样子。
2. 在回调函数中使用this
下一个问题发生在我们传递一个方法(使用this
作为参数)作为回调函数时。例如:
var getCarBrand = car.getBrand.bind(car); getCarBrand(); // 输出:Nissan
即使我们使用car.getBrand
,我们实际上只得到附加到按钮对象的函数getBrand()
。将参数传递给函数是隐式赋值,因此这里发生的情况与上一个示例几乎相同。不同之处在于,现在car.getBrand
不是显式赋值,而是隐式赋值。结果几乎相同——我们得到的是一个普通函数,绑定到按钮对象。换句话说,当我们在一个对象上执行一个方法时,该对象与最初定义该方法的对象不同,this
关键字不再指向原始对象,而是指向调用该方法的对象。参考我们的例子:我们在el
(按钮元素)上执行car.getBrand
,而不是它最初定义的car
对象。因此,this
不再指向car
,而是指向el
。如果我们想保持对原始对象的引用不变,同样,我们需要使用bind()
方法将getBrand()
函数显式绑定到car
对象。
var car = { brand: "Nissan", getBrand: function() { console.log(this.brand); } }; var el = document.getElementById("btn"); el.addEventListener("click", car.getBrand);
现在,一切按预期工作。
3. 在闭包内使用this
this
的上下文可能出错的另一个情况是我们在闭包内使用this
。考虑以下示例:
el.addEventListener("click", car.getBrand.bind(car));
这里的输出是undefined
,因为闭包函数(内部函数)无法访问外部函数的this
变量。最终结果是this.brand
等于window.brand
,因为内部函数中的this
绑定到全局对象。为了解决这个问题,我们需要将this
绑定到getBrand()
函数。
var car = { brand: "Nissan", getBrand: function() { var closure = function() { console.log(this.brand); }; return closure(); } }; car.getBrand(); // 输出:undefined
这种绑定等同于car.getBrand.bind(car)
。另一种流行的修复闭包的方法是将this
值赋值给另一个变量,从而防止意外更改。
var car = { brand: "Nissan", getBrand: function() { console.log(this.brand); } }; var getCarBrand = car.getBrand; getCarBrand(); // 输出:undefined
这里,this
的值可以赋值给_this
、that
、self
、me
、my
、context
、对象的伪名称或任何其他适合你的名称。关键是保留对原始对象的引用。
ECMAScript 6 的救援
在前面的示例中,我们看到了所谓的“词法this
”的入门知识——当我们将this
值赋值给另一个变量时。在ECMAScript 6中,我们可以使用类似但更优雅的技术,通过新的箭头函数来实现。箭头函数不是由function
关键字创建的,而是由所谓的“胖箭头”运算符(=>
)创建的。与普通函数不同,箭头函数从其直接封闭作用域获取this
值。箭头函数的词法绑定无法被覆盖,即使使用new
运算符也是如此。现在让我们看看如何使用箭头函数来替换var self = this;
语句。
function(){ console.log(this.brand); }
关于this
需要记住的内容
我们看到this
关键字,就像其他任何机制一样,遵循一些简单的规则,如果我们很好地了解这些规则,那么我们可以更自信地使用该机制。因此,让我们快速回顾一下我们已经学到的内容(来自本文和前文):
this
指向全局对象:this
指向父对象。call()
或apply()
或bind()
调用函数时,this
指向传递给这些方法的第一个参数。如果第一个参数为null
或不是对象,则this
指向全局对象。new
运算符调用函数时,this
指向新创建的对象。this
依赖于词法作用域并指向父对象。了解这些简单明了的规则,我们可以轻松预测this
将指向什么,如果它不是我们想要的,我们就知道可以使用哪些方法来修复它。
总结
JavaScript 的this
关键字是一个难以掌握的概念,但只要多加练习,你就能掌握它。我希望本文和我的上一篇文章能作为你理解的基础,并在下次this
让你头疼时成为有价值的参考。
JavaScript this
关键字的常见问题解答 (FAQs)
(此处省略了FAQs部分,因为篇幅过长,且与前面内容高度重复。FAQs部分内容已在前面详细解释。)
以上是掌握JavaScript的最后一步的详细内容。更多信息请关注PHP中文网其他相关文章!