This keyword in JavaScript. Complete * manual

* most likely, I missed something, but I'm sure the comments will tell me this

I am writing this article for my personal needs. It is planned that it will contain the answers to all the questions that students ask me on this topic. If it is useful to someone else - great.

image

Content.

  1. Introduction
  2. Misconceptions about this
  3. How to determine the value of this

Introduction


The this is one of the most confusing features of the JavaScript language. Coming from Java, it was intended to help in the implementation of OOP. I wrote in Java for some time, and I must say that during this time, maybe I once doubted what this is equal to in a particular place in the code. In JavaScript, such doubts can arise every day - at least until the moment you learn a few simple but non-obvious rules.

Misconceptions about this


There are several common misconceptions regarding this keyword. I want to quickly dispel them before I get to the point.

this is a lexical context.

This impression often arises among beginners. It seems to them that this is an object in which, as properties, all variables in this scope are stored. This misconception stems from the fact that in one particular case, roughly speaking, this is so. If we are at the highest level, then this equals window (in the case of a regular script in the browser). And as we know, all variables declared at the top level are available as window properties.

In general, this is not true. This is easy to verify.

 function test(){ const a = "     ,   "; console.log(this.a); } test(); //  ,    

this is the object to which the method belongs

Again, this is true in many specific cases, but not always. The important thing is how the function is called, and not whether it is a property of some object. This is clear even from a very simple logic: suppose the same function is a property of two objects simultaneously.

 const f = function(){ console.log(this); } const object1 = { method: f }; const object2 = { method: f }; 

So which of these objects will be her this?

Important! Even if an object is created using ES6 classes, this does not guarantee at all that its method will always have this . Don’t be fooled by the resemblance to classes from “normal” languages.

this is a Jedi technique that, once learned, needs to be used everywhere

If possible, avoid using this . The only case when it is fully justified is OOP. In other places, using this is not just evil, but usually an overkill confusing the code. In essence, this is an additional, “minus the first” argument to the function, which is passed there in a complicated, non-obvious way to the beginner. With high probability, it can be replaced by the usual argument, and it will only get better.

How to determine the value of this


Here I will try to give a rigorous and concise algorithm with which even an inexperienced coder will be able to understand what this is equal to in his particular case. More verbose explanations will be hidden under the spoilers, so as not to clutter up the visual space.

  1. Are we inside a function?


    Comment
    In top-level code (not inside any function), this always refers to a global object. In the case of a regular script in the browser, this is a window object. But in general, there are different cases.
  2. Are we inside the arrow function?

    • Yes: the value of this is the same as in the function one level higher (i.e. containing this one). Return to the previous step and repeat the algorithm for it. If the function is not contained in any other, this is a global object.
    • No: see the next paragraph.

    Comment
    One of the main features of arrow functions is the so-called “lexical this ”. This means that the value of this in the arrow function is determined solely by where (in what lexical context) it was created, and does not depend on how it was subsequently called. Sometimes this is not what we need, but more often it makes arrow functions very convenient and predictable.
  3. Is this function called as a constructor (using the new operator)?

    • Yes: this refers to a new object that is “under construction”.
    • No: see the next paragraph.

    Comment
    Perhaps it’s worth separately specifying the case when it comes to the constructor of the inherited ES6 class. Then, before calling super() , this does not have a value (accessing it will cause an error), and after calling super() it equals the new object of the parent class.
  4. Is this function created using the bind ?

    • Yes: the value of this is equal to the value of the first argument that we passed to the bind method when creating this function.
    • No: see the next paragraph.

    Comment
    The bind method creates a copy of the function, fixing this and, optionally, the first few arguments for it. In fact, this creates not just a copy of the function, but, I quote, an “exotic BoundFunction object”. Its exoticism is manifested, in particular, in the fact that by a repeated call to bind we can no longer change this . Therefore, strictly speaking, the answer in this paragraph should have been formulated as follows: if so, then this is equal to the first argument of the first call to bind , which led to the creation of this function.
  5. Is this function passed somewhere as a callback or handler?

    • Yes: the Lord alone knows what this will equal when summoned. Go read the documentation for the thing that will cause it.
    • No: see the next paragraph.

    Comment
    For a non-arrow and bound function, the value of this depends on the circumstances in which it was called. If you do not call it in person, but pass it somewhere, then this value may or may not be substituted with an unknown value to you.

    Examples:

     `use strict` document.addEventListener('keydown', function(){ console.log(this); }); //    this === document.    DOM- this  currentTarget [1, 2, 3].forEach(function(){ console.log(this); }); //         ,  undefined. ?     . 
  6. This function is called using the apply or call ?

    • Yes: in this case, this equals the first argument passed to the corresponding method.
    • No: see the next paragraph.

    Comment
    Another way to explicitly set this . More precisely, two. However, in terms of this no difference between apply and call , the only difference is how the rest of the arguments are passed.
  7. Is this function obtained as the value of an object property and is called immediately?

    • Yes: this equals the above object.
    • No: see the next paragraph.

    Comment
    Actually, from this mechanism (as well as from experience working with other languages), the legs grow of the belief that " this is the object whose method we called." Perhaps I’ll just write the code.

     'use strict'; const object1 = { method: function(){ console.log(this); } } const object2 = { method: object1.method } object1.method(); //    object1 -           object1['method'](); //  .        object2.method(); //    object2 -  " ",      ,         
  8. Does the code execute in strict mode? ('use strict', ES6 module)

    • Yes: this equals undefined .
    • No: this is equal to a global object.

    Comment
    If we get to this point, then this not set by any of the mechanisms that allow it to be set. There are various misconceptions about how else this can be passed. For example, during interviews they often tell me this kind of thing:

     const obj = { test: function(){ (function(){ console.log(this); })(); //      } } obj.test(); //  ,     obj.   

    Or, as I said in the “misconceptions” section, many people think that if a function is a method of an object created using ES6 classes, then this in it will always be equal to this object. This is also not true.

    So, if we get to this point, it means that the other circumstances of calling our function do not matter. And it all comes down to whether we are in strict mode or not.

    Historically, as a "default" this , a global object was passed to such functions. This approach was later found unsafe. ES5 introduced a strict mode that fixes many problems of earlier versions of ECMAScript. It is included with the 'use strict' directive at the beginning of a file or function. In this mode, the "default" value of this is undefined .

    In ES6 modules, strict mode is enabled by default.

    There are also other mechanisms for including strict mode, for example, in NodeJS, strict mode for all files can be enabled with the --use-strict flag.


That, in general, is all. Determining the value of this , of course, is less simple than we would like, but also not as difficult as it might seem. Learn this algorithm as a multiplication table - and you will never have problems with this again. So it goes.

PS User Aingis suggested that when using the with construct, the object passed into it as a context replaces the global object. Perhaps I will not enter this into my classifier, because the chance to meet with in 2019+ is rather small. But in any case, this is an interesting point.

PPS User rqrqrqrq suggested that new higher priority than bind . The corresponding revision to the classifier has already been made. Thank!

Source: https://habr.com/ru/post/464163/


All Articles