为什么在JavaScript中使用“ This”

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变量,我们可以看到callbindapply方法,可在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:

在上面的代码片段中,我们定义了一个新的构造函数,并分别对其进行了descriptionrandomFunction属性和方法。 我们使用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 calling this 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 。 我有时会选择catthanos嘿嘿。

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仍然可以证明棘手,弄明白最好的办法是找出其绑定的是在函数执行期间进行操作,并仔细追溯上下文。 随时留下您的反馈和意见。 编码愉快!!

翻译自: https://scotch.io/tutorials/why-this-in-javascript

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值