如果你曾使用JavaScript库做过开发,那么你可能已经注意到一个名为 this的特定关键字。虽然 this在JavaScript中非常常见,但是完全理解this关键字的原理以及在代码中如何使用对相当一部分的开发者来说着实不易。在这篇文章中,我将帮你深入理解 this及其工作机制。
在开始之前,请确保已安装 Node。然后,打开命令行终端并运行 node 命令。
全局环境中的“this”
this的工作机制理解起来并不是很容易。我们通过将 this置于不同环境下,分别来理解 this是如何工作的。首先看一下 global环境。
在全局环境中, this相当于全局对象 global。
1 |
this === global; |
但这只在 node中才有效。如果我们将相同的代码放在js文件中运行,得到的输出为false。
如果想测试效果,可以创建一个名为 index.js的文件,包含以下代码:
1 |
console.log(this === global); |
然后使用 node命令运行此文件:
1 |
$ node index.js |
原因是在JavaScript文件中, this等同于 module.exports
而不是 global 。
函数中的“this”
函数中的 this值通常是由函数的调用方来定义。因此,每次执行函数,函数内的 this值可能都不一样。
在 index.js文件中,编写一个非常简单的函数,来检查 this是否等于global对象。
1 |
function () { |
如果我们使用 node运行此代码,得到的输出为 true 。如果在文件的顶部添加 “use strict”,并再次运行它,将会得到一个 false输出,因为现在的 this值为 undefined 。
为了进一步解释这一点,让我们创建一个简单的函数来定义超级英雄的真实姓名和英雄名字
1 |
function Hero(heroName, realName) { |
请注意,这个函数并不是以严格模式
编写的。 node运行此代码,并不会得到我们预期的“Superman”和“Clark Kent”,但它只会给我们一个 undefined 。
背后的原因是,由于函数不是以严格模式编写的, this指向global对象。
如果在严格模式
下运行此代码,我们会收到报错,因为JavaScript不允许将属性 realName和 heroName赋给 undefined 。其实这是一件好事,因为它阻止我们创建全局变量。
另外,以大写形式书写函数名意味着我们需要将它视为构造函数,使用 new运算符来调用。用下面的代码替换上面代码段的最后两行
1 |
onst superman = new Hero("Superman", "Clark Kent"); |
再次运行 node index.js命令,现在会得到你预期的输出。
构造函数中的“this”
JavaScript没有特定的构造函数。我们所能做的就是使用 new运算符将函数调用转换为构造函数调用,如上一节所示。
构造函数被调用时,会创建一个新对象并将其设置为函数的 this参数。构造函数会隐式的返回这个对象,除非我们明确的返回了另外一个对象。
在 hero函数内部添加下面的 return语句:
1 |
return { |
如果我们再次运行 node命令,我们会看到 上面的 return语句覆盖了构造函数调用。
return语句不会覆盖构造函数调用的唯一情形是,return语句返回的不是一个对象。在这种情况下,对象将包含原始值。
方法中的“this”
当函数作为对象的方法被调用时, this指向的是该对象,也称为函数调用的接收器(receiver)。
假设 hero对象有一个 dialogue方法 ,那么 dialogue中的 this值指向 hero本身。此时的 hero也被称为 dialogue方法调用的接收者。
1 |
const hero = { |
这个例子非常简单,但在实际情况中,我们的方法很难跟踪接收器。在 index.js的末尾添加以下代码段。
1 |
const saying = hero.dialogue; |
如果我将 dialogue的引用存储在另一个变量中,并将该变量作为函数调用。 node运行代码 , this将返回 undefined ,因为该方法已经丢失了接收器。 this此时指向 global ,而不是 hero 。
当我们将一个方法作为回调传递给另一个方法时,通常会丢失接收器。我们可以通过添加包装函数或使用 bind
方法将 this与特定对象绑定来解决这个问题。
call() 和 apply()
虽然函数的 this值是隐式设置的,我们在调用函数时也可以使用 call()和 apply()明确设置 this参数。
我们重构一下前面章节的代码片段,如下所示:
1 |
function dialogue () { |