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?



    • Yes: see the next item.
    • No: this



      is equal to a global object .


    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!



All Articles