它一直在 Javascript 中使用,但它指的是什么通常是个谜。在 Javascript 中,它的this
工作方式与其他编程语言完全不同——而且它的工作方式也不同,具体取决于您是否使用use strict
模式。
如果你发现它很难,你并不孤单。让我们看看究竟是如何this
工作的,并消除关于它在各种情况下的含义的任何混淆。
这是什么在 Javascript 中#
this
是 Javascript 中的关键字,它指的是特定上下文中的一个属性或一组属性。我们使用的上下文this
改变了它的属性。在全局上下文中,this
指的是全局对象——在浏览器中是window
,但globalThis
在 Node.JS 和其他 Javascript 实现中是。
console.log(this); // The same as console.log(window);
在任何函数或代码之外,情况总是如此。但是,在不同的地方,this
意味着不同的东西。
这在 Javascript 函数中#
在函数中,this
仍然引用全局对象。如果我们this
在一个函数中引用,它会默认引用window
or globalThis
对象:
console.log(this); // The same as console.log(window); function myFunction() { console.log(this); // The same as console.log(window); } myFunction();
然而,在strict mode模式下this
,函数内部是未定义的。
"use strict" console.log(this); // The same as console.log(window); function myFunction() { console.log(this); // This is undefined! } myFunction();
用 call() 解决#
起初这有点令人困惑,但这样做的原因是因为我们需要添加一个this
对象myFunction
——严格模式下的 Javascript 不会将其默认为全局对象。为此,我们必须使用call()
. 在下面的例子中,我已经变成myObject
了我们的this
变量:
"use strict" console.log(this); // The same as console.log(window); let myObject = { firstName: "John", lastName: "Doe", age: 76 } function myFunction() { console.log(this.firstName); } myFunction.call(myObject); // this.firstName is defined as "John", so it will console log John myFunction(); // this.firstName will be undefined, and this will throw an error.
call()
运行myFunction
并附myObject
加到this
关键字。如果我们不使用调用,而只是运行myFunction()
,那么该函数将返回一个错误,因为this.firstName
它是未定义的。您还可以调用一个空的函数this
,然后您可以将数据附加到您的函数内部。
这给了我们一个新的空间来定义我们对象的变量,而不是被来自全局对象this
的数据污染:this
"use strict" console.log(this); // The same as console.log(window); function myFunction() { this.firstName = 'John'; console.log(this.firstName); // This will be "John" } myFunction.call({});
严格模式下的不同行为
如您所见,行为会因我们是否使用严格模式而大不相同——因此在两种模式之间更改代码之前进行一些测试很重要。
Call and Apply
有时您可能会看到call()
与称为 的函数互换使用apply()
。这两个函数非常相似,因为它们都调用具有指定this
上下文的函数。唯一的区别是apply()
,如果函数有参数,则采用数组,而call()
一个接一个地采用每个参数。
例如:
"use strict" let otherNumbers = { a: 10, b: 4 } function multiplyNumbers(x, y, z) { return this.a * this.b * x * y * z } // Both will return the same result, the only difference // being that apply() uses an array for arguments. multiplyNumbers.call(otherNumbers, 1, 2, 3); multiplyNumbers.apply(otherNumbers, [ 1, 2, 3 ]);
使用 bind() 简化这个过程
实现类似行为的另一种方法call()
是使用bind()
. 与call()类似,bind()
, 更改this
函数的值,只是它会永久更改。这意味着您不必经常使用bind()
– 您只需使用一次。
这是一个示例,我们将对象永久绑定到我们的函数,从而this
永久更新 – 我们只需将其定义为一个新函数。在下面的示例中,我们定义了一个名为 的新函数boundFunction
,它是我们永久绑定到它myFunction
的。myObject
因此,当我们调用控制台日志时,它将显示“John”。这和call不同,call是我们每次使用一个函数都需要调用的。
"use strict" console.log(this); // The same as console.log(window); let myObject = { firstName: "John", lastName: "Doe", age: 76 } function myFunction() { console.log(this.firstName); } let boundFunction = myFunction.bind(myObject); // this will bind this to myObject permanently. boundFunction(); // since we used bind, this will now be set to myObject, every time we call boundFunction() - so it will return John.
箭头符号函数和这个
Javascript 中箭头符号函数的一个关键特性是它们不包含this
上下文。这意味着他们this
从父母那里继承。例如,假设我们处于严格模式并同时定义了箭头函数和“普通”风格的函数。对于箭头函数,this
将被继承,但对于其他函数,this
将保持未定义状态!
试试看!
构造函数和这个
另一个有趣的事情this
是,当在构造函数(即使用new
关键字的函数)中使用时,构造函数的返回本质上会覆盖this
. 因此,例如,如果我们运行以下命令,虽然我们设置this.name
为John,但返回的name
值为Jack:
试试看!
this在对象上下文中#
在对象上下文中,using this
指的是对象。例如,假设我们在一个名为 的对象中运行一个函数obj
,它指的是this.aProperty
– this
,在这种情况下,指的是obj
:
let obj = { aProperty: 15, runFunction: function() { console.log(this.aProperty); // Refers to 15 } } obj.runFunction(); // Will console log 15, since this refers to obj
如果您使用get()
/set()
符号也是如此:
"use strict" let obj = { aProperty: 15, runFunction: function() { console.log(this.aProperty); // Refers to 15 }, set updateProp(division) { this.aProperty = this.aProperty / division; // this.aProperty refers to 15 console.log(this.aProperty); } } obj.updateProp = 15; // Will divide aProperty by 15, and console log the result, i.e. 1
与事件监听器一起使用#
Javascript 的另一个怪癖this
是,当使用事件侦听器时,this
引用事件被添加到的 HTML 元素。在下面的示例中,我们将点击事件添加到 ID 为“hello-world”的 HTML 标记:
document.getElementById('hello-world').addEventListener('click', function(e) { console.log(this); });
如果我们然后点击我们的#hello-world HTML 元素,我们将在我们的控制台日志中看到:
<div id="hello-world"></div>
与类一起使用#
在本节中值得注意的是,Javascript 中的类只是底层函数。这意味着我们在函数中看到的很多功能都适用于类。
默认情况下,类将this
设置为类实例本身。在下面的例子中,我们可以看到这个动作——两者runClass.name
都runClass.whatsMyName
返回John。
class myClass { whatsMyName() { return this.name; } get name() { return "John"; } } const runClass = new myClass(); console.log(runClass.name); // Returns "John" console.log(runClass.whatsMyName); // Returns "John"
唯一的例外是静态项不会添加到this
. 所以如果我们定义一个static
前面有关键字的函数,它就不会 on this
:
class myClass { getMyAge() { return this.whatsMyAge(); } static whatsMyAge() { return this.age; } get name() { return "John"; } get age() { return 143 } } const runClass = new myClass(); console.log(runClass.whatsMyAge()); // Throws an error, since runClass.whatsMyAge() is undefined console.log(runClass.getMyAge()); // Throws an error, since this.whatsMyAge() is undefined
值得注意的是,默认情况下,类始终处于严格模式 – 因此this
其行为方式与类中默认情况下严格函数的行为方式相同。
结论#
在 Javascript 中,this
可以表示不同的意思。在本文中,我们介绍了它在不同上下文(函数、类和对象)中的含义。我们已经介绍了如何使用bind()
,call()
以及为您的函数apply()
添加不同的this
上下文。
我们还介绍了如何this
在严格模式和非严格模式下使用。在此之后,我希望this
稍微揭开神秘面纱。