[Issue 764] You don’t understand JS: What is this?

image
Preface


Near the end of the year, I believe the next article is exactly what you need. This article is translated and authorized to share by the author of the front-end morning reading class column @HetfieldJoe.

ps: If you want to see the code, you can click on the picture to view


The text starts here~


This is you do not understand JS: this and object prototype Chapter 1: What is this?


One of the most confusing mechanisms in JavaScript is the this keyword. It is a special identifier keyword that is automatically defined in the scope of each function, but even some experienced developers are confused about what it points to.


Any sufficiently advanced technology is no different from magic. - Arthur C. Clarke


JavaScript's this mechanism is actually not that advanced, but developers always quote this sentence in their brains to express "complexity" and "confusion". There is no doubt that if there is no clear understanding, this may be in your confusion. It seems to be a total magic.


Note: The word "this" is a very common pronoun in general discussion. Therefore, especially in oral discussions, it is difficult to determine whether we are using "this" as a pronoun or as an actual keyword identifier. For clarity, I will always use this to represent special keywords, and in other cases use "this" or this or this.


Why use this?

If this mechanism is so puzzling for those experienced JavaScript developers, then someone will ask why this mechanism is useful? Doesn't it cause more trouble than benefit? Before explaining how it is useful, we should first look at why it is useful.


Let us try to show the motivation and purpose of this:

image


If how this code snippet works confuses you, don't worry! We will explain it soon. Just put these questions aside briefly so that we can explore why more clearly.


This code snippet allows the identify() and speak() functions to reuse multiple environment objects (me and you) instead of defining separate versions of functions for each object.


In contrast to using this, you can explicitly pass the environment object to identify() and speak().

image.png


However, this mechanism provides a more elegant way to implicitly "pass" an object reference, leading to cleaner API design and easier reuse.


The more complex your usage model, the clearer you will see: passing the execution environment as an explicit parameter is usually messier than passing this execution environment. As we explore objects and prototypes, you will see how useful a set of functions can automatically refer to the appropriate execution environment objects.


confused

We will soon begin to explain how this actually works, but we must first abandon some misunderstandings-it does not actually work.


Confusion arises when developers think about the name "this" too literally. This usually produces two kinds of conjectures, but both are wrong.


itself

The first common tendency is to think that this points to the function itself. At least, this is a grammatically reasonable guess.


Why do you want to refer to itself inside the function? The most common reason is a situation like recursion (invoking itself inside a function), or an event handler that will unbind itself the first time it is called.


Developers who are new to the JS mechanism usually think that using a function as an object (all functions in JavaScript are objects!) allows you to store state (values ​​in properties) between method calls. This is certainly possible and has some limited usefulness, but the rest of this book will explain many other patterns that provide better places to store state than function objects.


In a while we will explore a pattern to show how this prevents a function from getting a reference to itself as we might assume.


Consider the following code, we are trying to track how many times the function (foo) is called:

image.png


foo.count is still 0, even though the four console.log statements clearly tell us that foo(..) was actually called four times. This failure comes from an overly literal interpretation of the meaning of this (in this.count++).


When the code executes foo.count = 0, it does add a count attribute to the function object foo. But for the this.count reference inside the function, this does not point to that function object at all. Even if the attribute name is the same, the root object is different, which creates confusion.


Note: A responsible developer should ask a question here: "If the count attribute I increment is not the one I thought, which count was incremented by me?". In fact, if he digs deeper, he will find that he accidentally created a global variable count (Chapter 2 explains how this happens), and its current value is NaN. Of course, once he discovers this unusual result, he will have a bunch of other questions: "How is it global? Why is it NaN instead of a correct count value?". (See Chapter 2)


Instead of stopping here to delve into why this reference does not seem to work as we expect, and answering those sharp and important questions, many developers simply avoid this problem altogether and turn to some other alternative solutions, such as creating another An object to hold the count attribute:

image.png


Although this method does "solve" the problem, unfortunately it simply ignores the real problem-lack of understanding of the meaning of this and its working method-instead retreats to the comfort of a more familiar mechanism. Area: lexical scope.


Note: Lexical scope is a perfect and useful mechanism; I'm not trying to belittle it in any way (see "Scope and Closures" in this series). But on the question of how to use this, you always rely on guessing, and you usually make mistakes. It is not a good reason to fall back to the lexical scope and never learn why this does not work with you.


In order to refer to itself from within a function object, it is generally not enough to pass this. You usually need to get a reference to the function object through a lexical identifier (variable) pointing to it.


Consider these two functions:

image.png


The first function, called the "named function", foo is a reference that can be used to refer to itself within it.


But in the second example, the callback function passed to setTimeout(..) has no name identifier (so it is called an "anonymous function"), so there is no proper way to refer to the function object itself.


Note: There is an old but now obsolete function in the function, and the frowning arguments.callee reference also points to the function object of the currently executing function. This reference is usually the only way an anonymous function can access the function object within itself. However, the best way is to avoid anonymous functions altogether, at least for functions that require self-referencing, and use named functions (expressions). arguments.callee has been deprecated and should no longer be used.


For our current example, another useful solution is to use the foo identifier as a reference to the function object everywhere instead of this at all:

image.png


However, this method similarly avoids the true understanding of this, and completely relies on the lexical scope of the variable foo.


Another way to solve the problem is to force this to point to the foo function object:

image


Instead of avoiding this, we accept it. We will explain more fully how this technique works, so if you are still a little confused, don't worry!


Its scope

The second common misunderstanding of the meaning of this is that it somehow points to the scope of the function. This is a tricky question, because in one sense it has the correct part, and in another sense, it is seriously misleading.


To be clear, this does not point to the lexical scope of the function in any way. The scope seems to be an object with all available identifiers as attributes, which is true internally. But the JavasScript code cannot access the scope "object". It is the internal implementation of the engine.


Consider the following code, which (failed) attempts to cross this boundary, using this to implicitly refer to the lexical scope of the function:

image


There is more than one error in this code snippet. Although it seems to be a deliberate mess, the code you see is extracted from the actual code exchanged in the public help forum community. It is hard to imagine how misleading the conjecture of this is.


First, try to reference the bar() function through this.bar(). It can almost be said that it happened to work, we will explain how it works in a while. The most natural way to call bar() is to omit the beginning of this. and only make a lexical reference to the identifier.


However, the developer who wrote this code tried to use this to build a bridge between the lexical scope of foo() and bar() so that bar() can access the variable a in the internal scope of foo(). Such a bridge is impossible. You cannot use this reference to find things in lexical scope. This is impossible.


Whenever you feel like you are trying to use this for a lexical scope query, remind yourself: there is no bridge.


What is this?

We have listed various incorrect assumptions, now let us focus on how this mechanism really works.


As we said earlier, this is not binding at writing, but binding at runtime. It depends on the context of the function call. This binding has nothing to do with the location of the function declaration, but with the way the function is called.


When a function is called, an activity record is created, also known as the execution environment. This record contains information such as where the function is called (call-stack), how the function is called, and what parameters are passed. One of the attributes of this record is the this reference that will be used during the execution of the function.


review

For JavaScript developers who have not taken the time to learn how the this binding mechanism works, this binding has always been a source of confusion. Guessing, trial and error, or blindly copying and pasting from Stack Overflow answers are not effective or correct ways to use this important mechanism.


In order to learn this, you must first learn what this is not, no matter what kind of conjecture or misunderstanding that misleads you. this is neither a reference to the function itself nor a reference to the lexical scope of the function.


This is actually a binding established when the function is called, and what it points to is completely determined by the calling point where the function is called.


Guess you like

Origin blog.51cto.com/15080028/2595046