SoFunction
Updated on 2025-03-01

The most complete collection and sorting of this traps in JavaScript (no one)

When someone asks you what JavaScript is characterized by, you may immediately think of a bunch of words such as single-threading, event-driven, object-oriented, etc., but if you really want to explain these concepts, you may not be able to explain them clearly. There is a saying: If you can't explain something clearly to a 6-year-old child, then you don't understand it yourself. This sentence may be a bit exaggerated, but it makes sense. Personally, I think that if you need to master a language, mastering its API is just learning the basics, and understanding the essence of this language is the key. When it comes to the essence of JavaScript, this, closures, scope chains, and functions are well deserved. This language has officially become extremely charming because of these things.

The title of the blog is "The Most Complete Collection of this Traps in JavaScript - None", and it is obvious that this blog explains this. I believe that people who have done JavaScript development have encountered many traps of this, and I have encountered many traps myself, but if you have to give a systematic summary, there is not enough foundation. Fortunately, when I woke up this morning to watch "Hacker News", I happened to see an analysis about JavaScript this: all this. So, in the spirit of learning and sharing, I decided to translate it into Chinese. The purpose of translation is definitely not to be a porter of nature. In this process, you will fully understand other people's works and deepen your understanding, and share good things with others, so that more learners can stand on the shoulders of giants to move forward. According to my own habits, I will add some explanations (quoted parts) to the translation process. After all, there are differences in the way Chinese and Western people think. Of course, the most complete description of the title of the article is not to be hyped, the article is very long.

JavaScript comes from a sound language, so you may think that this in JavaScript and other object-oriented languages, like this in Java, refer to values ​​stored in instance properties. This is not the case. In JavaScript, it is best to regard this as the backpack of Boggart in Harry Potter, which has unfathomable magic.

The following section is what I want my colleagues to know when using this in JavaScript. There is a lot of content, and it was summarized by me after studying for several years.

This is often used in JavaScript, and the following details are introduced in each case. Here I would like to first introduce the concept of host environment. When a language is running, it requires an environment called the host environment. For JavaScript, the most common host environment is a web browser. The browser provides an environment for JavaScript to run. In this environment, some interfaces need to be provided so that the JavaScript engine can connect with the host environment. The JavaScript engine is where JavaScript code is truly executed. Common engines include V8 (currently the fastest JavaScript engine, produced by Google), and JavaScript core. The JavaScript engine mainly does the following:

  • A set of rules related to the host environment;
  • JavaScript engine kernel (basic syntax specifications, logic, commands and algorithms);
  • A set of built-in objects and APIs;
  • Other agreements.

But the environment is not unique, that is, JavaScript can not only run in the browser, but also in other programs that provide the host environment. The most common one is nodejs. Also as a host environment, nodejs also has its own JavaScript engine - V8. According to the official definition:
is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications

global this

In the browser, this is equivalent to a window object globally.

<script type="text/javascript">
   (this === window); //true
</script>

In the browser, declaring a variable with var is equivalent to adding attributes to this or window in a global scope.

 <script type="text/javascript">
   var foo = "bar";
   (); //logs "bar"
   (); //logs "bar"
 </script>

If you do not use var or let (ECMAScript 6) when declaring a variable, you are adding or changing the attribute value to the global this.

 <script type="text/javascript">
   foo = "bar";
 
   function testThis() {
    foo = "foo";
   }
 
   (); //logs "bar"
   testThis();
   (); //logs "foo"
 </script>

In a node environment, if you use REPL (Read-Eval-Print Loop, referred to as REPL: read-evaluation-output, it is a simple, interactive programming environment) to execute the program, this is not the most advanced namespace, the most advanced one is global.

> this
{ ArrayBuffer: [Function: ArrayBuffer],
 Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
 Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
 ...
> global === this
true

In a node environment, if a js script is executed, this starts with an empty object as the highest level namespace within the global scope. At this time, it is not equivalent to global.

Script content:

 (this);
 (this === global);

REPL run script:

 $ node 
 {}
 false

In the node environment, in a global scope, if you use REPL to execute a script file, declaring a variable with var will not add this variable to this just like in the browser.

 :
 
 var foo = "bar";
 ();
 
 $ node 
 undefined

But if you do not use REPL to execute script files, but execute code directly, the result is the same as in the browser (Divine Pit)

 > var foo = "bar";
 > 
 bar
 > 
 bar

In the node environment, when running script files with REPL, if var or let are not used when declaring variables, this variable will be automatically added to the global object, but will not be automatically added to this object. If you execute the code directly, it will be added to global and this at the same time

 
 
 foo = "bar";
 ();
 ();
 
 $ node 
 undefined
 bar

The above eight situations may be confused. To sum up, this is the boss in the browser, which is equivalent to a window object. If you declare some global variables (regardless of wherever), these variables will be used as attributes of this. In node, there are two ways to execute JavaScript code. One is to directly execute the written JavaScript file, and the other is to directly execute a line of code inside. Global is the boss of the way to directly run lines of JavaScript code, and this is equivalent to it. In this case, it is similar to the browser, that is, it declares that some global variables will be automatically added to the boss global, and will also be added to this by the way. However, the direct script file in node is different. The global variables you declare will not be automatically added to this, but will be added to the global object. So the same is that within the global scope, global variables belong to the boss after all.

function this
Whether in the browser environment or the node environment, except in the DOM event handler or thisArg is given (this will be discussed next), if you do not use new to call, using this in the function will refer to this global scope.

 <script type="text/javascript">
   foo = "bar";
 
   function testThis() {
     = "foo";
   }
 
   (); //logs "bar"
   testThis();
   (); //logs "foo"
 </script>


foo = "bar";

function testThis () {
  = "foo";
}

();
testThis();
();
$ node 
bar
foo

Unless you use strict mode, this will become undefined.

 <script type="text/javascript">
   foo = "bar";
 
   function testThis() {
    "use strict";
     = "foo";
   }
 
   (); //logs "bar"
   testThis(); //Uncaught TypeError: Cannot set property 'foo' of undefined 
 </script>

If you use new in front of the call function, this will become a new value, which is separated from global this.

 <script type="text/javascript">
   foo = "bar";
 
   function testThis() {
     = "foo";
   }
 
   (); //logs "bar"
   new testThis();
   (); //logs "bar"
 
   (new testThis().foo); //logs "foo"
 </script>

I prefer to call the new value an instance.

This in the function is actually relatively easy to understand. If we use this in a function, we need to pay attention to the way we call the function. If the function is called in a normal way, this refers to the global this. If we add a new, the function becomes a constructor, and we create an instance, this refers to this instance, which is very similar to other object-oriented languages. In addition, one thing that is very common to write JavaScript is to bind event handlers, that is, such as ('click', fn, false). If this is needed in fn, this refers to the object corresponding to the event handler, that is, button.

prototype this
Every function you create is a function object. They will automatically obtain a special property prototype, which you can assign to this property. When you call a function in the new way, you can access the value you assigned to the prototype through this.

 function Thing() {
    ();
 }
 
  = "bar";
 
 var thing = new Thing(); //logs "bar"
 (); //logs "bar"

When you use new to create multiple instances for your function, these instances share the values ​​you set for prototype. For the following example, when you call, the same value will be returned unless you rewrite your own in an instance

 function Thing() {
 }
  = "bar";
  = function () {
   ();
 }
  = function (newFoo) {
    = newFoo;
 }
 
 var thing1 = new Thing();
 var thing2 = new Thing();
 
 (); //logs "bar"
 (); //logs "bar"
 
 ("foo");
 (); //logs "foo";
 (); //logs "bar";
 
  = "foobar";
 (); //logs "foo";
 (); //logs "foobar";

This in the instance is a special object. You can think of this as a way to get the value of a prototype. When you directly add attributes to this in an instance, the attributes in the prototype with the same name will be hidden. If you want to access this property value in the prototype instead of the property value you set yourself, you can do so by deleting the properties you added in the instance.

 function Thing() {
 }
  = "bar";
  = function () {
   ();
 }
  = function (newFoo) {
    = newFoo;
 }
  = function () {
   delete ;
 }
 var thing = new Thing();
 ("foo");
 (); //logs "foo";
 ();
 (); //logs "bar";
  = "foobar";
 (); //logs "foobar";
 delete ;
 (); //logs "bar";

Or you can also directly obtain the value you need by referring to the prototype of the function object.

 function Thing() {
 }
  = "bar";
  = function () {
   (, );
 }
 
 var thing = new Thing();
  = "foo";
 (); //logs "foo bar";

Instances created through a function will share the value of the prototype property of the function. If you assign an Array to the prototype of this function, then all instances will share the Array, unless you rewrite the Array in the instance, in this case, the Array of the prototype of the function will be hidden.

 function Thing() {
 }
  = [];
 
 
 var thing1 = new Thing();
 var thing2 = new Thing();
 ("foo");
 (); //logs ["foo"]

Assigning an Array to a prototype of a function is usually a wrong approach. If you want each instance to have their own Array, you should create it in the function instead of in the prototype.

 function Thing() {
    = [];
 }
 
 
 var thing1 = new Thing();
 var thing2 = new Thing();
 ("foo");
 (); //logs ["foo"]
 (); //logs []

In fact, you can link the prototypes of multiple functions to form a prototype chain, so this will magically search up the prototype chain until you find the value you need to reference.

 function Thing1() {
 }
  = "bar";
 
 function Thing2() {
 }
  = new Thing1();
 
 
 var thing = new Thing2();
 (); //logs "bar"

Some people use the features of prototype chains to mimic classic object-oriented inheritance in JavaScript. Any statement that assigns this to a function used to build a prototype chain will hide the same properties upstream of the prototype chain.

 function Thing1() {
 }
  = "bar";
 
 function Thing2() {
    = "foo";
 }
  = new Thing1();
 
 function Thing3() {
 }
  = new Thing2();
 
 
 var thing = new Thing3();
 (); //logs "foo"

I like to call functions assigned to prototype methods. In the example above, I have used methods like logFoo. These methods have the same prototype, i.e., create the original function of these strengths. I usually call these original functions constructors. Using this in the method defined in prototype will affect this upstream of the prototype chain of the current instance. This means that when you assign this value directly, you hide the same attribute value upstream of the prototype chain. Any method of this instance will use the latest value instead of the same value defined in the prototype.

 function Thing1() {
 }
  = "bar";
  = function () {
   ();
 }
 
 function Thing2() {
    = "foo";
 }
  = new Thing1();
 
 
 var thing = new Thing2();
 (); //logs "foo";

In JavaScript you can nest functions, that is, you can define functions in functions. Nested functions can capture variables of parent functions through closures, but this function does not inherit this

 function Thing() {
 }
  = "bar";
  = function () {
   var info = "attempting to log :";
   function doIt() {
     (info, );
   }
   doIt();
 }
 
 
 var thing = new Thing();
 (); //logs "attempting to log : undefined"

This in doIt is a global object or is undefined under strict mode. This is the root cause of many people who are not familiar with JavaScript to fall into this trap. Things get really bad in this case, just like you pass the method of an instance as a value, and pass the value as a function parameter to another function but don't pass the instance to the function. In this case, the environment inside a method becomes global scope, or undefined under strict mode.

 function Thing() {
 }
  = "bar";
  = function () { 
   ();  
 }
 
 function doIt(method) {
   method();
 }
 
 
 var thing = new Thing();
 (); //logs "bar"
 doIt(); //logs undefined

Some people like to capture this into a variable first, usually this variable is called self to avoid the above situation.
Bloggers like this method very much

 function Thing() {
 }
  = "bar";
  = function () {
   var self = this;
   var info = "attempting to log :";
   function doIt() {
     (info, );
   }
   doIt();
 }
 
 
 var thing = new Thing();
 (); //logs "attempting to log : bar"

But it doesn't work when you need to pass a method as a value to a function.

 function Thing() {
 }
  = "bar";
  = function () { 
   var self = this;
   function doIt() {
     ();
   }
   doIt();
 }
 
 function doItIndirectly(method) {
   method();
 }
 
 
 var thing = new Thing();
 (); //logs "bar"
 doItIndirectly(); //logs undefined

You can solve this problem by passing everything in instances and methods to functions through bind. bind is a function defined on the function object of all functions and methods.

 function Thing() {
 }
  = "bar";
  = function () { 
   ();
 }
 
 function doIt(method) {
   method();
 }
 
 
 var thing = new Thing();
 doIt((thing)); //logs bar

You can also use apply and call to call methods or functions in a new context.

 function Thing() {
 }
  = "bar";
  = function () { 
   function doIt() {
     ();
   }
   (this);
 }
 
 function doItIndirectly(method) {
   method();
 }
 
 
 var thing = new Thing();
 doItIndirectly((thing)); //logs bar

You can use bind to replace this of any function or method, even if it is not assigned to the initial prototype of the instance.

 function Thing() {
 }
  = "bar";
 
 
 function logFoo(aStr) {
   (aStr, );
 }
 
 
 var thing = new Thing();
 (thing)("using bind"); //logs "using bind bar"
 (thing, ["using apply"]); //logs "using apply bar"
 (thing, "using call"); //logs "using call bar"
 logFoo("using nothing"); //logs "using nothing undefined"

You should avoid returning anything inside the constructor, as this may replace the instance that should have been returned.

 function Thing() {
   return {};
 }
  = "bar";
 
 
  = function () {
   ();
 }
 
 
 var thing = new Thing();
 (); //Uncaught TypeError: undefined is not a function

Strangely, if you return a primitive value in the constructor, the above situation does not happen and the return statement is ignored. It is best not to return any type of data in the constructor you will call through new, even if you know what you are doing. If you want to create a factory pattern, create an instance through a function, do not use new to call the function at this time. Of course this suggestion is optional.

You can avoid using new by using it, so that you can also create an instance.

 function Thing() {
 }
  = "bar";
 
 
  = function () {
   ();
 }
 
 
 var thing = ();
 (); //logs "bar"

In this case, the constructor will not be called

 function Thing() {
    = "foo";
 }
  = "bar";
 
 
  = function () {
   ();
 }
 
 
 var thing = ();
 (); //logs "bar"

Because the characteristic of not calling constructors is very useful when you want to rewrite constructors through prototype chains in your inheritance mode.

 function Thing1() {
    = "foo";
 }
  = "bar";
 
 function Thing2() {
   (); //logs "bar"
   (this);
   (); //logs "foo"
 }
  = ();
  = function () {
   ();
 }
 
 var thing = new Thing2();

object this
In an object's function, you can refer to other properties of this object through this. It is different to use new to create a new instance.

 var obj = {
   foo: "bar",
   logFoo: function () {
     ();
   }
 };
 
 (); //logs "bar"

Note that new is not used, and function calls are not used to create an object. You can also bind the function to it as an instance.

 var obj = {
   foo: "bar"
 };
 
 function logFoo() {
   ();
 }
 
 (obj); //logs "bar"

When you use this in this way, it will not break out of the current object. Only attributes with the same direct parent element can share variables through this

 var obj = {
   foo: "bar",
   deeper: {
     logFoo: function () {
       ();
     }
   }
 };
 
 (); //logs undefined

You can directly refer to the properties you need through the object

var obj = {
  foo: "bar",
  deeper: {
    logFoo: function () {
      ();
    }
  }
};

(); //logs "bar"

DOM event this
In an HTML DOM event handler, this always points to the HTML DOM node to which the handler is bound

 function Listener() {
   ("foo").addEventListener("click",
    );
 }
  = function (event) {
   (this); //logs "<div ></div>"
 }
 
 var listener = new Listener();
 ("foo").click();

Unless you switch context by binding

 function Listener() {
   ("foo").addEventListener("click", 
     (this));
 }
  = function (event) {
   (this); //logs Listener {handleClick: function}
 }
 
 var listener = new Listener();
 ("foo").click();

HTML this
In the properties of the HTML node, you can place JavaScript code, this points to this element

<div  onclick="(this);"></div>
 <script type="text/javascript">
 ("foo").click(); //logs <div ...
 </script>

override this
You can't rewrite this because it's reserved words.

function test () {
   var this = {}; // Uncaught SyntaxError: Unexpected token this 
 }

eval this
You can access this through eval

function Thing () {
}
 = "bar";
 = function () {
  eval("()"); //logs "bar"
}

var thing = new Thing();
();

This creates a security issue, unless there is no other way to avoid this issue.

When creating a function through Function, you can also access this

function Thing () {
}
 = "bar";
 = new Function("();");

var thing = new Thing();
(); //logs "bar"

with this
You can add this to the current execution environment by using with, and you do not need to pass this when reading and writing this property.

function Thing () {
 }
  = "bar";
  = function () {
   with (this) {
     (foo);
     foo = "foo";
   }
 }
 
 var thing = new Thing();
 (); // logs "bar"
 (); // logs "foo"

Many people think that using this is not good because with itself is controversial.

jQuery this
Like the event handler of HTML DOM element nodes, in many cases this JQuery points to HTML element nodes. This works in event handlers and some convenient methods, such as $.each

<div class="foo bar1"></div>
 <div class="foo bar2"></div>
 <script type="text/javascript">
 $(".foo").each(function () {
   (this); //logs <div class="foo...
 });
 $(".foo").on("click", function () {
   (this); //logs <div class="foo...
 });
 $(".foo").each(function () {
   ();
 });
 </script>

thisArg this
If you have used or lo-dash, you may know that many library methods can pass instances through a function parameter called thisArg, which will serve as the context of this. For example, this applies to _.each. Native JavaScript also allows functions to pass a thisArg parameter in ECMAScript 5, such as forEach. In fact, the use of bind, apply and call mentioned earlier has created the opportunity to pass thisArg parameter to the function. This parameter binds this to the object you are passing.

function Thing(type) {
    = type;
 }
  = function (thing) {
   (, thing);
 }
  = function (arr) {
  (, this); // logs "fruit apples..."
  _.each(arr, , this); //logs "fruit apples..."
 }
 
 var thing = new Thing("fruit");
 (["apples", "oranges", "strawberries", "bananas"]);

This makes the code more brief because it avoids the use of bind statements, function nesting and this temporary storage.