The introduction
After more than two months of intermittent reading of JavaScript classic little Red Book – JavaScript Advanced Programming (4th edition)
After reading it in detail, I found that the whole book is full of knowledge, and at first glance, the details of each chapter are slightly vague.
So I urge myself to plan to write the focus of each chapter to deepen my impression and understanding, and record what I have learned, thought and understood. To facilitate their own use of the computer’s quick search keywords for rapid positioning and learning, but also hope to help students in need.
If you want to learn systematically and carefully, of course, or read the original book is better, I also strongly recommend oh! The content here is for personal review and summary only.
Hint: some of the chapters that are not personally important or popular will be cut
Variables, scope, and memory
Ecma-262 states that JavaScript variables are loose, and that a variable is simply the name of a particular value at a particular point in time. Therefore, the values and data types of variables can change during the script life cycle. Such variables are interesting, powerful, and of course problematic. The following are the highlights of this chapter
- Use raw and reference values through variables
- Understand the execution Context
- Understand garbage collection
4.1 Original and Reference Values
ECMAScript variables contain two different types of data: raw and reference values.
- Raw value: The simplest data.
- The previous chapter discussed the six primitive values in ES6: Undefined, Null, Boolean, Number, String, and Symbol.
- The variables that hold the original values are accessed by value, and we operate on the actual values stored in the variables.
- Reference value: An object composed of multiple values.
- Unlike other languages, JavaScript does not allow direct access to memory locations, so you cannot directly manipulate the memory space in which an object resides.
- When you manipulate an object, you’re actually manipulating a reference to the object rather than the actual object itself.
- Save reference values for variables that are accessed by reference
In many languages, strings are represented as objects and are therefore considered reference types. ECMAScript breaks a convention.
4.1.1 Dynamic Properties
Raw and reference values are defined in much the same way, creating a variable and assigning a value to it. But reference values can add, modify, and delete their properties and methods at any time. The original value cannot have attributes and methods, but no errors are reported because of this.
// Reference values can manipulate properties and methods dynamically
let person = new Object(a); person.name ="lindada";
console.log(person.name); // "lindada"
// Raw values cannot manipulate properties and methods dynamically
let name = "lindada";
name.age = 27;
console.log(name.age); // undefined
Copy the code
Primitive types can be initialized using only the primitive literal form. If you use the new keyword, JavaScript creates an instance of type Object, but behaves like the original value. Here is the difference between the two initializations:
let name1 = "lindada";
let name2 = new String("lindada2");
name1.age = 21;
name2.age = 22;
console.log(name1.age); // undefined
console.log(name2.age); / / 22
console.log(typeof name1); // string
console.log(typeof name2); // object
Copy the code
4.1.2 duplicate values
The original and reference values are also different when assigned by variable.
-
The original value is copied to the location of the new variable. The two variables are used independently of each other
let num1 = 5; let num2 = num1; num1 = 6; console.log(num2); / / 5 Copy the code
-
When a reference value is assigned from one variable to another, the stored value is copied to the location of the new variable. The difference is that the copied value is actually a pointer to an object stored in heap memory. After the copy is complete, the two variables actually point to the same object.
let obj1 = {}; let obj2 = obj1; obj1.name = "lindada"; // reflect to obj2. console.log(obj2.name); // "lindada" Copy the code
4.1.3 Passing Parameters
Arguments to all functions in ECMAScript (raw values, reference values) are passed by value. Operations are consistent with their respective copy value rules.
-
Raw value parameter
function addTen(num) { num += 10; return num; } let count = 20; let result = addTen(count); console.log(count); // Still 20, not reference passing changes with it. console.log(result); / / 30 Copy the code
-
Referential value parameter
function setName(obj) { obj.name = "lindada"; obj = new Object(a); obj.name ="lindada2"; } let person = new Object(a); setName(person);console.log(person.name); // "lindada" // a reference that does not change the reference value is still a pointer to the reference value person. Therefore, reference value parameters are also passed by value. Copy the code
Arguments to ECMAScript functions are local variables.
4.1.4 Determine the type
ECMAScript has several ways to determine what type a variable is. How to use it depends on their own needs.
-
Typeof: Determines the data type in the original value or whether it is an original value or a reference value.
let s = "lindada"; let b = true; let i = 22; let u; let n = null; let o = {}; console.log(typeof s); // string console.log(typeof b); // boolean console.log(typeof i); // number console.log(typeof u); // undefined console.log(typeof n); // object console.log(typeof o); // object Copy the code
-
Instanceof: If the known variable is a reference value, it is used to determine whether the prototype chain is a given reference class or a custom class.
const person = {}; const color = []; const pattern = new RegExp('/ * /'); // Custom class function Person(name, age) { this.name = name; this.age = age; } var person = new Person("lindada".18); console.log(person instanceof Object); // true console.log(color instanceof Array); // true console.log(pattern instanceof RegExp); // true console.log(person instanceof Person); // true // instanceof checks the raw value, which always returns false. console.log(123 instanceof Number); // false Copy the code
-
Object. The prototype. ToString. Call () : the judgment of an Object to belong to what kind of built-in types
// Function type Function fn(){ console.log (" test "); }Object.prototype.toString.call(fn); // "[object Function]" // Date type var date = new Date(a);Object.prototype.toString.call(date); // "[object Date]" // Array type var arr = [1.2.3]; Object.prototype.toString.call(arr); // "[object Array]" // Regular expressions var reg = /[hbc]at/gi; Object.prototype.toString.call(reg); // "[object RegExp]" Copy the code
4.2 Execution context and scope
-
The context of variables or functions determines what data they can access and how they behave.
- The global context is the outermost context, which in the browser is the Window object.
- Therefore, all global variables and functions defined by var become properties and methods of the Window object.
- Top-level declarations using lets and const are not defined in the global context, but have the same effect on scope-chain resolution.
-
A context is destroyed after all of its code has been executed, including all variables and functions defined on it (the global context is destroyed before the application exits).
-
Each function call has its own context. When code execution flows into a function, the context of the function is pushed up and down a stack. After the function completes execution, the stack pops up the context of the function, returning control to the previous execution context.
-
The execution flow of an ECMAScript program is controlled through this context stack.
-
When the code in the context executes, it creates a chain of scopes for the variable object.
- This chain of scopes determines the order in which the code at each level of context accesses variables and functions.
- The variable object of the context in which the code is executing is always at the front of the scope chain.
- If the context is a functionActive objectsAs a variable object
- Active objects initially have only one definition variable: arguments (this variable is not available in the global context).
- The next variable object in the scoped chain is the context containing the variable object, and so on to the global context (always the last variable object in the scoped chain).
-
The following example shows the above rules.
var color = "blue"; function changeColor() { let anotherColor = "red"; function swapColors() { // This context can access color, anotherColor, and tempColor. let tempColor = anotherColor; anotherColor = color; color = tempColor; } // Access color, anotherColor. swapColor(); } // Only color can be accessed here. changeColor() Copy the code
The connections between contexts are linear and ordered. Each context can search for variables and functions in the upper-level context, but no context can search in the lower-level context.
Function parameters are considered variables in the current context and therefore follow the same access rules as other variables below.
4.2.2 Variable declaration
Since ES6, the new let and const variable declaration keywords have become the preferred variable declarations.
4.2.2.1 Function scope declaration using var
-
When a variable is declared using var, it is automatically added to the nearest context. In a function, the closest context is the local context of the function.
-
If a variable is initialized undeclared, it is automatically added to the global context.
function add(num1, num2) { var sum = num1 + num2; sum2 = num1 - num2; } let result = add(20.10); / / 30 console.log(sum); // An error was reported. The variable is not in the current context. console.log(sum2); Sum2 is added to the global context after the call to add() Copy the code
Undeclared variables can cause a lot of problems in JavaScript, so be sure to declare variables before initializing them. In strict mode, an error is reported when a variable is initialized undeclared.
-
Var declarations are carried to the top of a function or global scope, a phenomenon called promotion. In practice, ascension leads to legitimate but strange phenomena.
// Global scope console.log(name); // undefined var name = "lindada"; // The variable is promoted // Function scope function() { console.log(name); // undefined var name = "lindada"; // The variable is promoted } Copy the code
4.2.2.2 Block-level scope declaration using let
-
The let variable declaration is similar to the var variable declaration, but its scope is block-level and is defined by the closest pair of curly braces {}, which is also a new concept in JavaScript.
- Such as if blocks, while blocks, function blocks, and even separate {} block scopes.
-
Let cannot be declared twice in the same scope (var overrides the declaration).
-
Let will not be promoted.
// block-level scope if (true) { // This variable is declared in the block-level scope let a; } console.log(a); // ReferenceError // Repeat the declaration var a = 3; var a = 4; console.log(a); { let a = 3; let a = 4; console.log(a); // SyntaxError } // The variable is promoted console.log(b); let b = 4; // ReferenceError Copy the code
Strictly speaking, let is also promoted in JavaScript runtime, but because of temporary dead zones, you can’t actually use let variables before declaration. Therefore, from the standpoint of writing JavaScript code, let is not promoted in the same way as VAR.
4.2.2.3 Using a const constant declaration
-
Variables declared using const must also be initialized to a value.
-
Once declared, new values cannot be reassigned at any point in its life cycle (but the object’s keys are not restricted).
- If you want to make the entire Object immutable, you can use object.freeze () to silence the Object modification failure.
-
The rest of the rules are similar to let variable declarations.
const a; // SyntaxError, constant declaration is not initialized. const b = 3; console.log(b); / / 3 b = 4; // TypeError, constant variables cannot be assigned. const o1 = {}; o1.name = 'lindada'; console.log(o1.name); // 'lindada' o1 = {}; // TypeError, constant variables cannot be reassigned. const o2 = Object.freeze({}); o2.name = 'lindada'; console.log(o1.name); // undefined Copy the code
Google’s V8 engine optimizes constant declarations, so if a variable is assigned only once, use as many const declarations as possible.
4.3 Garbage Collection
JavaScript is a language that uses garbage collection, which means the execution environment is responsible for managing memory while the code executes. Unlike languages such as C and C++ that need to track memory usage, JavaScript takes this burden off the developer, allocating memory and recycling idle resources through automatic memory management.
The simple idea is to periodically determine which variable is no longer used, and then free up memory.
But it is an undecidable problem to determine whether a variable is still useful. However, in the history of browsers, two major markup strategies have been used: tag cleanup and reference counting.
4.3.1 Mark cleaning
This is the most common JavaScript garbage collection strategy. When a variable is entered into a context, it is marked with a marker that exists in the context. Variables are also marked out of context when they are out of context.
There are many ways to tag variables. For example, when a variable comes into context, it reverses a bit; Or you can maintain lists of variables in and out of context, and you can mark variables by moving them from one list to the other. (But it’s not the tagging process that matters; it’s the strategy.)
When the garbage collector runs, it marks all variables stored in memory. It then strips out all variables in the context, and all variables referenced by variables in the context. Variables marked after this point are to be deleted. (The reason is that no variables in context can access them.) The garbage collector then does a memory cleanup, destroying all tagged values and reclaiming them.
By 2008, Internet Explorer, Firefox, Opera, Chrome, and Safari all adopted tag cleaning (or variants of it) in their JavaScript implementations, with differences in how often they ran garbage collection.
4.3.2 Reference Counting
Another, less commonly used garbage collection strategy is reference counting, where the idea is to keep track of how many times each value is referenced.
When you first declare a variable and assign it a reference value, the number of references to that value is 1. If it’s assigned to another variable again, it increments by 1.
If the variable holding a reference to this value is overwritten by another value, the reference value is reduced by 1.
So the next time the garbage collector runs, it frees the memory with the zero reference value.
Reference counting was first adopted by Netscape Navigator 3.0 and soon ran into a serious problem: circular references
function porblem() {
let obj1 = new Object(a);let obj2 = new Object(a); obj1.someOtherObject = obj2; obj2.anotherObject = obj1; }// After the function ends, the number of references is always 2 under the reference-counting policy, although neither object is in scope.
Copy the code
You need to explicitly set the object referenced by the loop to NULL (which severs the relationship between the variable and its previously referenced value) to clear the loop reference.
4.3.3 performance
Garbage collection programs run periodically, and frequent calls can cause performance losses, so scheduling garbage collection is important. So the best thing to do is to write your code so that whenever you start collecting garbage, it ends as quickly as possible.
According to a 2016 blog post from the V8 team: “After a full garbage collection, V8’s heap growth strategy determines when to recycle again based on the number of active objects plus some margin.”
Similar to a certain threshold is reached to call the collection. If IE7 starts at the same threshold as IE6, but if the garbage collector reclaims less than 15% of the allocated memory, the thresholds for these variables, literals, or array slots are doubled (indicating that there are many variables throughout the lifetime). If 85% of allocated memory is reclaimed at one time, the threshold is reset to the default
It is possible (but not recommended) to actively trigger garbage collection in some browsers. In IE, the window.collectgarbage () method immediately triggers garbage collection. In Opera7 and later, a call to window.opera.collect() also starts the garbage collector.
4.3.4 Memory Management
In programming environments that use garbage collection, memory management is usually not a concern for developers. But keeping the memory footprint to a small value can lead to better page performance. Therefore, the best way to optimize memory footprint is to ensure that only the necessary data is saved during code execution.
4.3.4.1 Dereferencing
Local variables are automatically dereferenced when they go out of scope, but globally-scoped values are not. This advice works best for global variables and properties of global objects.
function createPerson(name) {
let localPerson = new Object(a); localPerson.name = name;return localPerson
}
// Object instance created globally
let globalPerson = createPerson("lindada");
// Unreference the globalPerson value
globalPerson = null;
Copy the code
It is worth noting, however, that dereferencing a value does not automatically cause the associated memory to be reclaimed. The key to dereferencing is to ensure that the associated value is no longer in context, so it will be collected in the next garbage collection.
4.3.4.2 Improve performance through const and let declarations
The addition of these two keywords in ES6 not only helps improve the code style, but also the garbage collection process. Because both const and let are block-scoped, they let the garbage collector free up memory much earlier than function or global scope.
4.3.4.3 Hiding classes and Deleting Operations
As of 2017, Chrome is the most popular browser and runs on the V8 JavaScript engine. V8 makes use of hidden classes when compiling interpreted JavaScript code into actual machine code. This may be important to you if your code is very performance-oriented.
As the code runs, V8 associates the objects created with the hidden classes to track their attribute properties, so that objects that share the same hidden class perform better.
function Article() {
this.title = "msg";
}
let a1 = new Article();
let a2 = new Article();
V8 is configured in the background so that the two class instances share the same hidden class
// Share the same constructor and stereotype
a2.author = "lindada";
delete a2.author;
// Add or remove attributes, however, and the two instances correspond to two different hidden classes
a2.author = null;
// The best example is to set unwanted attributes to null. This keeps the hidden classes unchanged and continues to be shared, and removes the reference values for the garbage collector to recycle.
Copy the code
4.3.4.4 Memory Leakage
Memory leaks in JavaScript are mostly caused by improper references.
-
Accidentally declare global variables
-
function setName() { name = 'lin' // Equivalent to window.name = 'Lin', should use a variable declaration. } Copy the code
-
Therefore, as long as the Window itself is not cleaned, it will not disappear, causing a memory leak
-
-
Timer problem
-
function name = 'Jake' setInterval(() = > {console.log(name) }, 100// If the timer is not cleared in timeCopy the code
-
The name referenced in the callback will occupy memory as long as the timer is running.
-
-
The closure problem
-
let outer = function() { let name = 'lin' return function() { return name } } Copy the code
-
In the closure example above, the name cannot be cleaned as long as outer exists. If the data is large, memory leaks can be a big problem.
-
4.3.4.5 object pool
The last thing to consider in order to improve JavaScript performance is “squeezing” the browser. This is how to reduce the number of times the browser performs garbage collection, so developers can indirectly control the conditions that trigger garbage collection.
In theory, if you can use allocated memory wisely and avoid unwanted garbage collection, you can preserve the performance lost by freeing memory.
-
Existing objects
-
function addVector(a, b) { let resultant = new Vector() resultant.x = a.x + b.x resultant.y = a.y + b.y return resultant } Copy the code
-
Calling this function creates a new object on the heap, modifies it, and returns it to the caller. If this function is called frequently, the garbage collection scheduler will notice that the object is being replaced quickly and will schedule garbage collection more often.
-
function addVector(a, b, resultant) { resultant.x = a.x + b.x resultant.y = a.y + b.y return resultant } Copy the code
-
You can use an existing object to avoid creating new objects repeatedly.
-
-
Object pooling
-
At some point during initialization, a pool of objects can be created to manage a collection of recyclable objects. Developers can request an object from the object pool and manipulate it, then return it to the object pool when the operation is complete. Because no object initialization occurs, garbage collection probes do not detect object turnover, and therefore the garbage collection program does not run as frequently.
-
// Pseudo-implementation of object pool // vectorPool is an existing object pool let v1 = vectorPool.allocate() let v2 = vectorPool.allocate() / / modify v1.x = 10 v1.y = 5 v2.x = -3 v2.y = -6 / / call addVector(v1, v2) // Release // If the object has attributes that reference other objects, then these attributes need to be set to NULL v1 = null v2 = null Copy the code
-
If the object pool allocates vectors on demand (creating new ones when they don’t exist and reusing them when they do), then the implementation is essentially a greedy algorithm with monotonously growing but static memory. Object pools must maintain all objects in some structure, so arrays are a good choice. Using arrays, however, must be careful not to incur additional garbage collection.
-
Note: Static configuration is an extreme form of optimization, and if your application is severely slowed down by garbage collection, consider using it to improve performance. But that’s not often the case. In most cases, this is premature optimization, so don’t worry about it.
summary
JavaScript variables can hold two types of values: original and reference values. They have the following characteristics:
- The original value
- The original value is fixed in size and therefore stored in stack memory.
- Copying the original value from one variable to another creates a second copy of the value.
- Reference value
- Reference values are objects stored in heap memory.
- Variables that contain reference values actually contain only a pointer to the corresponding object, not the object itself.
- Copying a reference value from one variable to another only copies Pointers, so both variables point to the same object.
- The typeof operator determines the original typeof a value, while the instanceof operator is used to ensure the reference typeof the value (that is, the prototype of the reference value).
- Object. The prototype. ToString. Call () returns the data type of the built-in objects, also can return the custom data types
Any variable exists in some execution context (called scope). This context (scope) determines the lifetime of variables and what parts of the code they can access, and the execution context can be summarized below.
- Execution context is divided into global context, function context and block-level context.
- Each time the code execution flow enters a new context, a chain of scopes is created to search for variables and functions.
- The local context of a function or block can access variables not only in its own scope, but also in any containing context or even in the global context.
- You can only look up, not down.
- The global context can only access variables and functions in the global context, and cannot access any data in the local context.
- A local context can access its own context data, as well as variables and functions in the global context through scope chains.
- The execution context of a variable is used to determine when memory is freed.
JavaScript is a programming language that uses garbage collection, so developers don’t need to do memory allocation and collection. The JavaScript garbage collector can be summarized as follows:
- Values that leave scope are automatically marked as recyclable and then deleted during garbage collection.
- The dominant garbage collection algorithm is tag cleaning, which marks values that are not currently applicable and then comes back to reclaim their memory.
- Reference counting is another garbage collection strategy that keeps track of how many times a value is referenced.
- JavaScript references no longer use this algorithm because reference counting has problems with circular references in code.
- Some older versions of IE still suffer from this algorithm because JavaScript accesses non-native JavaScript objects.
- Dereferencing variables not only eliminates circular references, but also helps with garbage collection. To facilitate memory reclamation, global objects, properties of global objects, and circular references should all be dereferenced when no longer needed.