Study Notes 6—Scope and Memory

1 Original value and reference value

The original value is the simplest data, and the reference value is an object composed of multiple values.
When assigning a value to a variable, the JavaScript engine must determine whether the value is a primitive value or a reference value. The variable that holds the original value is accessed by value, because we are manipulating the actual value stored in the variable. The reference value is an object stored in memory. Unlike other languages, JavaScript does not allow direct access to the memory location, so it cannot directly manipulate the memory space where the object is located. When you manipulate an object, you actually manipulate the reference of the object, not the actual object itself. For this reason, the variable holding the reference value is accessed by reference.

2 Copy value

In addition to the storage method, the original value and the reference value are also different when they are copied through variables. When assigning an original value to another variable through a variable, the original value will be copied to the location of the new variable.

let num1 = 5;
let num2 = num1;

num1 contains the value 5. When num2 is initialized to num1, num2 will also get the value 5. This value is completely independent of the 5 stored in num1. These two variables can be used independently without interfering with each other.
When assigning a reference value from one variable to another, the value stored in the variable will also be copied to the location of the new variable. The difference is that what is copied here is actually a pointer to an object stored in the heap memory. After the operation is complete, the two variables actually point to the same object, so changes on one object will be reflected on the other object:

let obj1 = new Object();
let obj2 = obj1;
obj1.name = "Nicholas";
console.log(obj2.name); //"Nicholas"

The variable obj stores an instance of a new object, and then this value is copied to obj2, at this time both variables point to the same object.

3 Passing parameters

The parameters of all functions in ECMAScript are passed by value, which means that the values ​​outside the function will be copied to the parameters inside the function, just like copying from one variable to another. If it is the original value, it is the same as the copy of the original value variable. If it is the reference value, it is the same as the variable copy of the reference value.

function setName(obj){
    
    
	obj.name = "Nicholas";
}
let person = new Object();
setName(person);
console.log(person.name); //"Nicholas"

4 Determine the type

In order to solve the problem of what type of object, ECMAScript provides the instanceof operator:

console.log(person instanceof Object);//变量person是Object么
console,log(colors instanceof Array); //变量colors是Array么
console.log(pattern instanceof RegExp); //变量pattern是RegExp么

If the variable is given an instance of a reference type, the instanceof operator returns true

5 Execution context and scope

About the understanding of the execution context: When programming, we generally define some preconditions for the program (environmental variables, global variables describing environmental changes, etc.). These "premises" are the above, and then write the code for each functional module. This is Below.
The context of variables or functions determines what data they can access and their behavior. Each context has an associated variable object, and all variables and functions defined in this context exist on this object. Although the variable object cannot be accessed through code, it is used for background processing of data.
There are only three types of execution context, global execution context, function context, and eval context; since eval is generally not used, we will not discuss it here.
The global context is the outermost context. In the browser, the global context is what we often call the window object, so all global variables and functions defined by var will become the properties and methods of the window object. Top-level declarations using let and const will not be defined in the global context, but the effect on scope chain resolution is the same. The context will be destroyed after all its code is executed, including all variables and functions defined on it (the global context will be destroyed before the application exits, such as closing the web page or exiting the browser).
Each function call has its own context. When the code execution flow enters the function, the context of the function is pushed onto a context stack. After the function is executed, the context stack will pop up the function context and return control to the previous execution context. The execution flow of the ECMAScript program is controlled through this context stack.
Scope: Refers to the collection of variables that you have access to.
During the execution of the code in the context, a scope chain of variable objects is created. This scope chain determines the order in which the code in all levels of context access variables and functions. The variable object of the context in which the code is being executed is always at the forefront of the scope chain. If the context is a function, its active object is used as a variable object. The active object initially has only one defined variable: arguments. The next variable object in the scope chain comes from the containing context, and the next object comes from the next containing context. By analogy until the global context, the variable object of the global context is always the last variable object in the scope chain.
Identifier resolution during code execution is accomplished by searching the identifier name level by level along the scope chain. The search process always starts from the front end of the scope chain, and then step by step, until the identifier is found.

var color = "blue";
function changeColor(){
    
    
	if(color === "blue"){
    
    
		color = "red";
	}else{
    
    
		color = "blue";
	}
}
changeColor();

For this example, the scope chain of changeColor() contains two objects: one is his own variable object (that is, the one that defines the arguments object), and the other is the variable object of the global context. The variable color can be accessed inside this function because it can be found in the scope chain.
In addition, variables defined in the local scope can be used to replace global variables in the local context:

        var color = "blue";

        function changeColor() {
    
    
            let anotherColor = "red";

            function swapColors() {
    
    
                let tempColor = anotherColor;
                anotherColor = color;
                color = tempColor;
            }
            swapColors();
        }
        changeColor();
        console.log(color);

The above code involves three contexts: the global context, the local context of changeColor() and the local context of swapColors(). There is a variable color and a function changeColor() in the global context. There is a variable anotherColor and a function swapColors() in the local context of changeColor(), but here you can access the variable color in the global context. There is a variable tempColor in the local context of swapColors(), which can only be accessed in this context. Neither the global context nor the local context of changeColor() can access tempColor. In swapColors(), you can access variables in the other two contexts because they are both parent contexts.

6 Garbage collection

JavaScript is a language that uses garbage collection, which means that the execution environment is responsible for managing memory during code execution. The basic idea is simple: determine which variable will no longer be used, and then release the memory it occupies. This process is periodic, that is, the garbage collection program will run automatically at regular intervals. The garbage collector must keep track of which variables will be used and which variables will not be used in order to reclaim the memory. How to mark unused variables may be implemented in different ways. But in the history of browser development, two main tagging strategies have been used: tag cleanup and reference counting.
(1) Mark cleanup The
most commonly used garbage collection strategy in JavaScript is mark cleanup. When a variable enters the context, for example, when a variable is declared inside a function, the variable will be marked with a mark that exists in the context. The variables in the context, logically speaking, should never release their memory, because as long as the code in the context is running, they may be used. When a variable leaves the context, it will also be marked as leaving the context.
There are many ways to tag variables. For example, when the variable enters the context, it reverses a certain bit, or you can maintain two variable lists "in context" and "not in context", and you can transfer variables from one list to Another list. The realization of the marking process is not important, the key is strategy.
When the garbage collection program runs, it will mark all the variables stored in the memory. Then it will remove all the variables in the context and the variable tags referenced by the variables in the context. After that, the marked variables are to be deleted, because any variables in the context cannot access them. The garbage collection program then does a memory cleanup, destroying all marked values ​​and reclaiming their memory.
(2) Reference counting
Another less common garbage collection strategy is reference counting. The idea is to record the number of times each value has been referenced. Declaring a variable and assigning it a reference value is that the number of references to this value is 1. If the same value is assigned to another variable, then the number of references increases by 1. Similarly, if the referenced variable is saved by other values If it is covered, the number of citations is reduced by one. When the number of references of a value is 0, it means that there is no way to access the value, so its memory can be safely recovered. The next time the garbage collector runs, it will release the memory whose reference count is 0.
Reference counting was first adopted by Netscape Navigator 3.0, but soon encountered a serious problem: circular references. The so-called circular reference means that object A has a pointer to object B, and object B also references object A, such as:

function problem(){
    
    
	let objectA = new Object();
	let objectB = new Object();
	objectA.someOtherObject = objectB;
	objectB.anotherObject = objectA;
}

In this example, object and objectB refer to each other through their respective attributes, which means that their reference counts are both 2. Under the tag cleanup strategy, this is not a problem, because after the function ends, these two objects are not in scope . Under the reference counting strategy, objectA and objectB will still exist after the function ends, because their reference count will never become 0.
(3) Static allocation and vector pool
In order to improve the performance of JavaScript, the last thing to consider is often to squeeze the browser. At this time, a key issue is how to reduce the number of times the browser performs garbage collection. Developers cannot directly control when garbage collection starts, but they can indirectly control the conditions that trigger garbage collection. In theory, if the allocated memory can be used reasonably while avoiding redundant garbage collection, then the performance lost due to the release of memory can be maintained.
One criterion for the browser to determine when to run the garbage collection program is the speed of object replacement. If many objects are initialized. Then all of a sudden it is out of scope, then the browser will use a more aggressive way to schedule the garbage collection program to run, which of course will affect performance:

function addVector(a,b){
    
    
	let resultant = new Vector();
	resultant.x = a.x + b.x;
	resultant.y = a.y + b.y;
	return resultant;
}

When this function is called, a new object is created on the heap, then it is modified, and finally it is returned to the caller. If the life cycle of this vector object is short, it will quickly lose all references to it and become a value that can be recycled. Adding this vector addition function is frequently called, then the garbage collection scheduler will find that the object replacement speed here is very fast, so it will schedule garbage collection more frequently. The solution to this problem is not to dynamically create vector objects. For example, you can modify the above function to use an existing vector object:

function addVector(a,b,resultant){
    
    
	resultant.x = a.x + b.x;
	resultant.y = a.y + b.y;
}

Of course, this requires instantiating the vector parameter resultant elsewhere, but the behavior of this function has not changed. So where can you create a vector without letting the garbage collection scheduler look at it?
One strategy is to use an object pool. At a certain moment of initialization, an object pool can be created to manage a group of recyclable objects. The application can request an object from this object pool, set its properties, use it, and then return it to the object pool after the operation is completed. Since no object initialization has occurred, the garbage collection probe will not find any object replacement, so garbage The recovery program will not run so frequently. P100

Guess you like

Origin blog.csdn.net/qq_43599049/article/details/113002876