1. Copy variable values

  1. Copying a value of a primitive type from one variable to another creates a new value on the object variable, and then copies that value to the location allocated for the new variable. As shown in figure 4-1
let num1 = 5;
let num2 = num1;
Copy the code

  1. When copying a value of a reference type from one variable to another, a copy of the value stored in the variable object is also made into the space allocated for the new variable. The difference is that a copy of this value is actually a pointer that points to a variable stored in the heap. Figure 4-2 shows this relationship between variables stored in a variable object and objects stored in the heap.
let obj1 = new Object();
let obj2 = obj1;
obj1.name = 'tony';
alert(obj2.name); // 'tony'
Copy the code

2. Execution environment and scope

The execution environment defines other data that variables or functions have access to, and determines their respective behavior. Each execution will have a variable object associated with it, where all variables and functions defined in the environment are stored.

The global execution environment is the most peripheral execution environment. In web browsers, the global execution environment is considered a Window object. All global variables and functions are created as properties and methods of the Window object.

After all code in an execution environment is executed, the environment is destroyed, along with all variables and function definitions stored in it (the global execution environment is not destroyed until the application exits, for example by closing a web page or browser)

Each function has its own execution environment. When the execution stream enters a function, the function’s environment is pushed onto an environment stack. After the function is executed, the stack pops up its environment, returning control to the previous execution environment. The flow of execution in ECMAScript programs is controlled by this convenient mechanism.

When code is executed in an environment, a scope chain of variable objects is created. The purpose of scope chains is to ensure orderly access to all variables and functions that the execution environment has access to.

At the front of the scope chain is always the variable object of the environment in which the code is currently executing. If the environment is a function, consider its activation object as a variable object. Active objects start out with just one variable, the Arguments object (which does not exist in the global environment).

The next variable object in the scope chain comes from the containing (external) environment, and the next variable object comes from the next containing environment. This continues to the global execution environment; The variable object of the global execution environment is always the last object in the scope chain.

var color = "blue"; function changeColor(){ var anotherColor = "red"; function swapColors(){ var tempColor = anotherColor; anotherColor = color; color = tempColor; // Here you can access color, anotherColor, and tempColor} // here you can access color and anotherColor, but not tempColor swapColors(); } // Only color changeColor() can be accessed;Copy the code

The above code involves three execution environments: the global environment, the local environment of changeColor(), and the local environment of swapColors(). The rectangles in Figure 4-3 represent a specific execution environment

The inner environment can access all the outer environments through the scope chain, but the outer environment cannot access any variables and functions in the inner environment.

3. Extend the scope chain

Although there are only two types of execution environment altogether — global and local (functions), there are other ways to extend the chain of scopes.

This is because some statements can temporarily add a variable object to the front of the scope chain, which is removed after code execution. This happens under two conditions.

Specifically, the scope chain is lengthened when the execution stream enters any of the following statements: 1. the catch block of a try-catch statement 2. the with statement

function bindUrl(){ var qs = "? debug=true"; with(location){ var url = href + qs; } return url; }Copy the code

4. Garbage collection

JavaScript has automatic garbage collection, which means that the execution environment is responsible for managing the memory used during code execution.

The idea behind this garbage collection mechanism is simple: find variables that are no longer in use, and free up memory. To do this, the garbage collector periodically performs this operation at fixed intervals (or at predetermined collection times during code execution).

Let’s examine the normal life cycle of a local variable in a function. Local variables exist only during the execution of a function.

In this process, local variables are allocated space in stack (or heap) memory to store their values. These variables are then used in the function until the function is finished executing. There is no need for local variables to exist, so they can be freed up for future use.

In this case, it is easy to determine whether the variable is still necessary; But not all cases are so easy to draw. The garbage collector must keep track of which variables are useful and which are not, and mark variables that are no longer useful, so they can be reclaimed in the future.

  1. Mark clear

The most common method of garbage collection in JavaScript is mark-and-sweep. When a variable is entered into the environment (for example, declaring a variable in a function), the variable is marked as entered into the environment. Logically, you can never free up memory occupied by variables that enter the environment, because they may be used whenever the execution stream enters the corresponding environment. When a variable leaves the environment, it is marked as “out of the environment.”

Variables can be marked in any way. For example, you can flip a particular bit to record when a variable entered the environment, or use a list of variables that entered the environment and a list of variables that left the environment to track which variable changed. At the end of the day, it doesn’t really matter how you mark variables, but what strategy you use.

At run time, the garbage collector marks all variables stored in memory (of course, it can be marked in any way). It then unflags variables in the environment and those referenced by variables in the environment. Variables tagged after this point are considered to be ready for deletion because they are no longer accessible to variables in the environment. Finally, the garbage collector completes the memory cleanup, destroying the tagged values and reclaiming the memory they occupy.

  1. Reference counting

Another, less common garbage collection strategy is called reference counting. The meaning of reference counting is to keep track of how many times each value is referenced. When a variable is declared and a reference type value is assigned to the variable, the number of references to the value is 1.

If the same value is assigned to another variable, the number of references to the value is increased by one. Conversely, if a variable containing a reference to that value takes another value, the number of references to that value is reduced by one.

When the number of references to this value goes to zero, there is no way to access the value, and the memory space it occupies can be reclaimed. This way, the next time the garbage collector runs, it frees the memory occupied by values with zero references.

All variables (both primitive and reference types) exist in an execution environment (also known as a scope), which determines the lifetime of the variables and which parts of the code can access them. Here are a few summary points about the implementation environment:

  1. Execution environment is divided into global execution environment (also known as global environment) and function execution environment.
  2. Each time a new execution environment is entered, a chain of scopes is created to search for variables and functions;
  3. The local environment of a function has access not only to variables in the function scope, but also to its containing (parent) environment and even to the global environment.
  4. The global environment can only access variables and functions defined in the global environment, and cannot directly access any data in the local environment.
  5. The execution environment of a variable helps determine when memory should be freed.

JavaScript is a programming language with automatic garbage collection, so developers don’t have to worry about memory allocation and reclamation. This can be summarized with a garbage collection routine for JavaScript.

  1. Values that leave scope are automatically marked as recyclable and therefore deleted during garbage collection.
  2. “Tag cleanup” is the prevailing garbage collection algorithm. The idea is to tag values that are not currently in use and then reclaim their memory.
  3. Another garbage collection algorithm is “reference counting,” the idea of which is to keep track of how many times all values are referenced.

JavaScript engines no longer use this algorithm; However, this algorithm can still cause problems when accessing non-native JavaScript objects (such as DOM elements) in IE.

  1. The “reference counting” algorithm can cause problems when there are circular references in your code.
  2. Dereferencing variables not only helps eliminate circular references, but also benefits garbage collection. To ensure efficient memory reclamation, global objects, global object properties, and circular reference variables that are no longer used should be dereferenced in a timely manner.