While JavaScript is a fun and powerful language, it can be tricky and requires a proper understanding of its underlying principles to mitigate common errors.
尽管JavaScript是一种有趣且功能强大的语言,但它可能会很棘手,并且需要正确理解其基本原理以减轻常见错误。
In this post, we shall be introducing you to the this
keyword, its behaviour and the hard choices behind it. These will be detailed using appropriate examples to better drive home the point.
在本文中,我们将向您介绍this
关键字,其行为以及其背后的艰难选择 。 将使用适当的示例详细介绍这些内容,以更好地说明这一点。
这是什么? ( What is this? )
Developers make the mistake of thinking that this
refers to the “scope” of the function within which it's encountered. This isn’t true because whenever a function is invoked, it runs in a new execution context until its execution is completed. Each execution context usually references an object and the value of that object translates to the value of this
.
开发人员错误地认为this
是指在其中遇到功能的“范围”。 这是不正确的,因为无论何时调用函数,它都会在新的执行上下文中运行,直到完成执行为止。 每个执行上下文通常都引用一个对象,并且该对象的值转换为this
的值。
For us to properly understand what this
is, we would first need to go over two important concepts:
为了让我们正确地理解this
是什么,我们首先需要回顾两个重要的概念:
- Context 语境
- Call-Site 呼叫网站
语境 (Context)
Commonly in the English language, once a noun is defined in a sentence, subsequent references to the subject noun will be done using a pronoun. Understand?
通常在英语中,一旦在句子中定义了名词,随后将使用代词对主题名词进行引用。 理解?
First Statement: Brad is a good boy. Second Statement: He is a very brilliant boy!
第一句话:布拉德是个好男孩。 第二句话:他是一个非常聪明的男孩!
Who is a brilliant boy? Brad... obviously!
谁是一个聪明的男孩? 布拉德...显然!
This is quite the same in JavaScript, the first statement creates the context, while this
functions like 'he' in the second statement and is used to reference the subject in the first statement.
这是在JavaScript不太一样,第一个语句创建上下文,而this
在第二条语句的功能,如“他”和用来引用在第一条语句的主题。
this
refers to the context of an executing function.
this
是指执行功能的上下文。
This would mean that during every function execution, the focus is on the new execution context which is redetermined whenever a function is invoked, and since the context holds a reference to an object, the value of this
is updated accordingly.
这意味着在每次执行函数期间,重点都放在新的执行上下文上,每当调用一个函数时都会重新确定该上下文,并且由于上下文包含对对象的引用,因此相应地更新了this
对象的值。
It is impractical to assume that the value of this
is fixed and always refers to the function’s scope, the value of this
is, in fact, dynamic and determined by how a function is called and where it is called:
假设this
函数的值是固定的,并且始终引用该函数的作用域是不切实际的,实际上, this
函数的值是动态的,并且由函数的调用方式和调用位置决定:
Different sections in a JavaScript program run in an execution context and maintain that context until there is a switch to a new context. The context of an executing function references the object that invoked it, therefore, a call to this
within a function refers to the invoking object.
JavaScript程序中的不同部分在执行上下文中运行并维护该上下文,直到切换到新上下文为止。 执行函数的上下文引用了调用它的对象,因此,在函数内this
函数的调用是指调用对象。
Since the context is really important to our understanding of this
, we need to know how/why the context switches during runtime:
由于上下文对于我们理解this
一点确实很重要,因此我们需要知道上下文在运行时如何/为什么切换:
上下文如何变化? (How does the context change?)
The current execution context in a JavaScript program usually points to the object that invokes a function and can be switched to another object by invoking the function with another object.
JavaScript程序中的当前执行上下文通常指向调用一个函数的对象,并且可以通过使用另一个对象调用该函数来切换到另一个对象。
呼叫网站 (Call-Site)
The place where a function is invoked in a JavaScript program is called the call-site.
This is also important in determining the value of this
because if the function isn’t invoked by any visible object, the current context of the call-site
would be responsible for setting the new execution context of the function.
在JavaScript程序中调用函数的位置称为call-site.
这也是确定的值很重要this
是因为如果该功能不受任何可见对象调用,那么当前情况下call-site
将负责设定功能的新的执行上下文。
In simpler terms, the value of this
represents the context of the initial executing function in a call-site and remains the same in any other call-site an invoking function doesn't provide context.
简单来说,的值this
表示在调用点的初始执行功能的上下文中,并保持在任何其他调用点的调用功能不提供上下文相同。
We can finally define this
based on all of the explanations presented above:
我们终于可以定义this
基础上所有上面给出的解释:
The value of the
this
keyword is dependent on the object upon which it is invoked and not the function itself or the function’s scope.
this
关键字的值取决于调用它的对象,而不取决于函数本身或函数的作用域。
The call-site
of the function is also important in determining what this
refers to.
该函数的call-site
在确定this
所指的内容方面也很重要。
此绑定 ( This bindings )
There are four bindings that are important in determining the value of this
in a JavaScript program. We will look at them in this section.
有四个绑定是在确定的价值重要this
在JavaScript程序。 我们将在本节中介绍它们。
默认绑定 (Default binding)
Let’s say we were writing a program and defined a function where we used the this
keyword, then we invoked the function in the global scope (outside of any function) of the program. What object do you think the this
keyword would reference runtime? Let’s see:
假设我们正在编写程序并在其中使用this
关键字定义了一个函数,然后在程序的全局范围(任何函数之外)中调用了该函数。 您认为this
关键字会引用什么对象? 让我们来看看:
let randomFunction = () => {
this.attribute = 'This is a new attribute to be created on the invoking object';
console.log(this);
}
randomFunction();
Good thinking! This
references the global object in the output below:
好想法! This
将在以下输出中引用全局对象:
In the code above, we created a random function that attaches a new property “attribute” to whichever object invokes it. Because the call-site
of the function invocation had its context referencing the global object (this is default), we see that in the output, the console logs a lot of properties that are attached to the global object (Window object) including the property that was created by the random function.
在上面的代码中,我们创建了一个随机函数,该函数将新属性“ attribute”附加到调用它的任何对象。 因为函数调用的调用call-site
的上下文引用了全局对象(这是默认设置),所以我们看到在输出中,控制台记录了许多附加到全局对象(Window对象)的属性,包括该属性由随机函数创建的。
This is the default binding of this
in JavaScript. When a function is called without an object, this
is by default bound to the current execution context, we will look at the other bindings below.
这是默认的结合this
在JavaScript中。 当一个函数被调用无一物, this
是默认绑定到当前执行的情况下,我们将看看下面的其他绑定。
By default,
this
is bound to the global object when invoked by a global function.默认情况下,当由全局函数调用时,
this
绑定到全局对象。
It is noteworthy that when we are coding in strict mode, this
holds the value of undefined in global functions and in anonymous functions that are not bound to any object.
值得注意的是,当我们以严格模式进行编码时, this
在全局函数和未绑定到任何对象的匿名函数中具有undefined的值。
显式绑定 (Explicit Binding)
Remember how we said that the execution context can be changed by invoking a function on an object? Well, there are explicit ways to easily achieve the same result, let’s look at them briefly:
还记得我们怎么说通过调用对象上的函数可以改变执行上下文吗? 好了,有一些明确的方法可以轻松实现相同的结果,让我们简要地看一下:
- Bind: This method creates a new function and when invoked sets its 'this' keyword to a provided value (passed as an argument). 绑定:此方法创建一个新函数,并在调用时将其“ this”关键字设置为提供的值(作为参数传递)。
- Call - This method calls a function and allows us to specify the context its
this
value should be bound to as an argument. This method accepts optional arguments. 调用-此方法调用一个函数,并允许我们指定应this
值绑定为参数的上下文。 此方法接受可选参数。 - Apply - This function is similar to
call()
with a difference of allowing us to pass in arguments as an array. Apply-此函数类似于call()
,不同之处在于允许我们将参数作为数组传递。
Let’s look at an example:
让我们看一个例子:
function randomFunction(){
console.log(this);
}
let newObj = {
description : "This is a new Object"
}
console.log(randomFunction.bind(newObj)());
console.log(randomFunction.call(newObj));
console.log(randomFunction.apply(newObj));
In the code above, we have explicitly bound this
in the random function to the newObj
variable and we can see that the call
, bind
and apply
methods are available on the Function prototype in JavaScript. They are all correctly bound in the output below:
在上面的代码中,我们已经明确地结合this
在随机函数的newObj
变量,我们可以看到call
, bind
和apply
方法,可在JavaScript中的函数原型。 它们都正确绑定在以下输出中:
隐式绑定 (Implicit Binding)
When an object defines a method (that calls this
) as a property and calls that method somewhere in a program, the this
within the method will be implicitly bound to that object. Let’s look at this example:
当对象将方法(称为this
)定义为属性并在程序中的某个位置调用该方法时,该方法中的this
将隐式绑定到该对象。 让我们来看这个例子:
let newObj = {
description : "This is a new Object",
randomFunction(){
console.log(this);
}
}
newObj.randomFunction();
This is a straightforward example that complies with our definition of this
above; the invoking object is implicitly bound to the method here and it logs the object when invoked:
这是一个简单的例子,符合我们this
的定义; 调用对象隐式绑定到此处的方法,并且在调用时记录该对象:
新装订 (New Binding)
If we have an object constructor and create a new object with it, the new object will have its this
value as a reference to the constructor from which it was created. This is the this
binding that happens when a new object is created using a constructor. Let’s look at an example:
如果我们有一个对象构造函数并使用它创建一个新对象,则新对象将使用其this
值作为对创建该对象的构造函数的引用。 这是this
结合使用构造函数创建一个新的对象时发生。 让我们看一个例子:
function newObj() {
this.description = "This is an object constructor"
this.randomFunction = () => {
console.log(this);
}
}
let anotherFunction = new newObj()
anotherFunction.randomFunction()
In the snippet above, we defined a new constructor function and gave it description
and randomFunction
property and method respectively. We created an instance with the anotherFunction
variable and invoked its randomFunction
method, here’s the output:
在上面的代码片段中,我们定义了一个新的构造函数,并分别对其进行了description
, randomFunction
属性和方法。 我们使用anotherFunction
变量创建了一个实例,并调用了其randomFunction
方法,这是输出:
Here, the logged object has the description
property that was defined on the object constructor to prove that this
references the constructor function.
在这里,记录的对象具有description
这是对对象的构造函数定义来证明财产this
引用的构造函数。
这个和那个 ( This and That )
The takeaway from everything that has been shown above is:
上面显示的所有内容的要点是:
Wherever
this
is defined in a function, it usually isn’t assigned a value until an object invokes the containing function.无论
this
在一个函数被定义,它通常是直到对象调用包含函数赋值。
You might think that the paragraph above is all you would ever need to refer to when you work with this
but there are scenarios where this
behaves in a weird way (cus JavaScript).
你可能会认为上面的段落,你会需要参考的时候和你一起工作的所有this
,但有某些场景下, this
行为与一个奇怪的方式(CUSJavaScript)。
Let’s look at a code snippet where we define a closure that references this
in JavaScript:
让我们看一下代码片段,其中定义一个在JavaScript中引用this
的闭包:
let newObj = {
description : "This is a new Object",
randomFunction(){
let a = 1
return function() {
console.log(this);
}
}
}
newObj.randomFunction()();
You would reasonably expect that the closure returns the value of this
as the object that invoked randomFunction
but let’s see what the output says:
你会有理由期待关闭返回的值, this
是调用该对象randomFunction
但让我们看到的输出这样说:
Wait, what? How does this
refer to the global object here? Wasn’t the outer function called by the newObj
object? Shouldn’t that have switched the execution context and updated the this
reference?
等一下 this
在这里如何指代全局对象? newObj
对象不是调用外部函数吗? 那不应该切换执行上下文并更新this
引用吗?
These are all valid questions, however, here’s something to note:
这些都是有效的问题,但是,请注意以下几点:
Closures cannot access their outer function’s
this
value by callingthis
within themselves.闭包无法通过在内部调用
this
值来访问其外部函数的this
值。
This is because the this
value of a function is only accessible by the function within which it is encountered and not its inner functions. Hence the this
of an anonymous closure will be bound to the global object where strict mode is not being used.
这是因为函数的this
值只能由遇到该函数的函数访问,而不能由其内部函数访问。 因此, this
一个匿名闭合的将被绑定到在不使用严格模式的全局对象。
How can we get the preferred behavior?
我们如何获得首选行为?
Here's a trick. Consider this code snippet:
这是一个把戏。 考虑以下代码片段:
var newObj = {
description : "This is a new Object",
randomFunction(){
var that = this;
return function() {
console.log(that);
}
}
}
newObj.randomFunction()();
Here, in randomFunction
we declare a new variable called that
and assign it the value of this
. This way, we can reference the this
value of the outer function within the closure since the scope of an outer function is always accessible by its inner functions.
在这里,在randomFunction
我们声明了一个名为that
的新变量that
并为其分配了this
的值。 这样,我们可以在闭包中引用外部函数的this
值,因为外部函数的范围始终可由其内部函数访问。
Here’s the output:
这是输出:
Great! We have referenced the this
value of the outer function in the closure by creating a that
variable.
大! 我们通过创建that
变量在闭包中引用了外部函数的this
值。
It should be noted that we could have called the variable anything at all but chose that
because so many JavaScript developers already find it convenient to call it that
. I sometimes choose cat
or thanos
hehe.
应当指出的是,我们可以叫在所有的变量什么,但选择that
因为如此多JavaScript开发人员已经发现可以方便地调用它that
。 我有时会选择cat
或thanos
嘿嘿。
Now you know this
... and that
.
现在你知道this
... ... that
。
A better way to handle this without declaring a new variable will be by using an arrow function. We have:
在不声明新变量的情况下处理此问题的更好方法是使用箭头函数。 我们有:
var newObj = {
description : "This is a new Object",
randomFunction(){
return () => console.log(this)
}
}
newObj.randomFunction()();
This produces the same result as declaring that
.
这将产生相同的结果,宣布that
。
结论 ( Conclusion )
We have explored the this
keyword in JavaScript clarifying its behavior and use in the process. We also looked at the scenario of closures where the this
logic may be elusive and we went over a fitting solution, however, there are situations where this
can still prove tricky, the best way to figure it out is to identify which of the bindings is in operation during the function execution and trace back the context carefully. Feel free to leave your feedback and comments. Happy coding!!
我们已经在JavaScript中探索了this
关键字,以阐明其行为和在此过程中的使用。 我们也看着关闭的情形,其中this
逻辑可能是镜花水月,我们过了一个合适的解决方案,但是,存在的情况下this
仍然可以证明棘手,弄明白最好的办法是找出其绑定的是在函数执行期间进行操作,并仔细追溯上下文。 随时留下您的反馈和意见。 编码愉快!!