JS optimization for front-end performance optimization

introduction

In today's technological world, whether it is a web page or an application, front-end development is undoubtedly crucial. With the development of the Internet, users have higher and higher requirements for the performance and loading speed of web pages. As front-end developers, we should strive for better performance optimization. Among them, JavaScript is the core language of front-end development, and its impact on web page performance cannot be ignored. This article will focus on JavaScript performance optimization, through a series of techniques and methods, to help readers understand how to optimize JavaScript code, so as to improve the loading speed and user experience of web pages. Whether you are a front-end developer just getting started or a veteran who has already experienced in the industry, you can learn some practical skills from this article. I hope this article can provide you with some help and inspiration. Let's delve into the mystery of JavaScript optimization for front-end performance optimization.

1. The browser loads the js file process

The browser's process of loading js file resources can be divided into the following stages:

  1. Initiate a request : The browser obtains the js file resource from the server by sending an HTTP request. This process involves network transmission, and network delay may be a relatively large overhead.

  2. Receive response : After receiving the request, the server returns a response message, which contains the content of the js file. The browser will parse the response header and receive the response body. The time required for this process depends on the size of the js file and the speed of network transmission.

  3. Parsing HTML : When the browser receives the js file, if there is a reference to the file in the HTML page, the browser will pause the parsing of the HTML, and download and parse the js file as soon as possible. At this time, the browser will execute the js engine to parse and compile the js file. The time this process takes depends on the size and complexity of the js file.

  4. Execute js code : When the js file is parsed and compiled, the browser will execute the js code in it. The execution speed of js code depends on the performance of the js engine and the complexity of the code. There may be some CPU overhead when executing js code.

  5. Update the page : If the js code manipulates the DOM structure of the page, the browser will re-render the page to reflect the update to the DOM. The time required for a DOM operation depends on the complexity of the operation and the performance of the browser.

Generally speaking, the process of loading js files usually involves multiple stages such as network transmission, parsing and compiling, executing and updating pages , and the overhead of each stage will be affected by different factors, including file size, network speed, code complexity wait.

2. Comparison of browser loading js and pictures

When the browser loads JS resources and image resources of the same size, it usually takes longer to load JS resources .

This is because there are several stages in the process of loading JS resources:

  1. DNS resolution : The browser obtains the IP address of the server through domain name resolution, and completes the positioning of resources.
  2. Establish connection : The browser establishes a TCP connection with the server, including the three-way handshake process.
  3. Send request : The browser sends a GET request to the server to request related resources.
  4. Receive response : The server returns a response, depending on the server speed and network conditions, it may take a while.
  5. Download resource : Once the response is received, the browser starts downloading the JS resource.
  6. Parsing and execution : After the download is complete, the browser starts parsing and executing the JS code.

As for the loading process of image resources, it is relatively simple and mainly includes the following stages:

  1. DNS resolution .
  2. Establish a connection .
  3. Send request .
  4. Receive the response .
  5. Download resources .

It can be seen that the loading of JS resources still needs to be parsed and executed after the download is completed, while the image resources do not need to be parsed and executed, so relatively speaking, the time-consuming of loading JS resources will be longer .

According to the specific network environment and server response speed, the following table is a possible time-consuming comparison table for different stages of loading:

stage JS resources are time consuming Image resource time-consuming
DNS resolution 20ms 20ms
establish connection 30ms 30ms
send request 10ms 10ms
receive response 50ms 50ms
download resource 100ms 100ms
parse and execute 200ms -
total time spent 410ms 210ms

It should be noted that the above data is only for reference, and the actual time consumption will be affected by factors such as network conditions, server response speed, and caching mechanism.

3. The ratio of js resources loaded by the browser to the total resource loading time

In general, the time required by the browser to load JS resources takes up a large portion of the time required to load all resources. This is because JavaScript is a scripting language that needs to be run on the client side, and it will directly affect the interaction and function realization of the page, so it will be time-consuming to load and execute JS resources. Usually between 10% and 30%

For example, suppose an actual website consists of an HTML page, a CSS stylesheet, and a JS file. During loading, the browser proceeds in the following order:

  1. First, the browser downloads the HTML page. This process is faster because there is less HTML content.

  2. Next, the browser parses the HTML page and finds references to CSS style sheets during the parsing process. The browser will start downloading the CSS style sheet file and apply it to the page after the download is complete. This process is faster than JS resources.

  3. After the CSS style is loaded, the browser will continue to parse the HTML page and find the reference of the JS file. At this point, the browser will start to download the JS file, and execute the JS script logic after the download is complete. Since JS files usually contain complex logic processing, as well as possible asynchronous operations and network requests, JS loading and execution time may be longer.

To sum up, for a website, the time required by the browser to load JS resources accounts for a large part of the time required to load all resources. Because the JS script plays a vital role in the page, involving key parts such as interaction and function realization, it takes a long time to load and execute. In some complex websites, JS resources may even take longer to load than other resources.

It should be noted that the specific situation of the page will also affect the time allocation ratio of each resource loading. For example, if the main content of a website is pictures, the loading time of picture resources may be longer, while the loading time of JS resources is relatively short. So this ratio will vary from site to site.

4. Overview of the compilation principle of v8

V8 is an open source JavaScriptengine, Googledeveloped by V8, for executing JavaScriptcode. Its compilation principle is based on just-in-time compilation ( Just-In-Time Compilation,JIT) technology.

The compilation process of V8 can be divided into three stages: analysis ( parsing), optimization ( optimization) and code generation ( code generation).

  1. Parsing phase :
    In the parsing phase, V8 parses JavaScriptthe code into a data structure called an abstract syntax tree ( Abstract Syntax Tree, AST). ASTIt is a way to represent code in a tree structure , and each node represents a component of the code, such as variables, functions, expressions, etc. The parsing phase also builds lexical scopeinformation about the lexical scope ( ) for subsequent optimization phases.

  2. Optimization phase :
    The optimization phase is the core part of the V8 engine. V8 uses a Just-In-Time Compilation,JITtechnique called just-in-time compilation ( ), which optimizes code before it is executed. V8 analyzes the execution characteristics of the code, trying to identify and infer the type and behavior of the code. Based on this information, V8 will perform a series of optimizations on the code, such as inline functions ( inlining), remove useless codes ( dead code elimination), generate native codes ( native code generation), etc. These optimizations can significantly improve JavaScriptcode execution efficiency.

  3. Code generation phase :
    In the code generation phase, V8 converts the optimized code into machine code for execution on the underlying system. V8 adopts a Code Cachingtechnology called cache execution ( ), which caches the compiled code and can be used directly in subsequent executions to avoid repeated compilation and improve performance.

It should be noted that V8 's optimization is based on just-in-time compilation, which means that V8 dynamically optimizes during code execution, rather than pre-optimizing in the static stage. By collecting code execution characteristics at runtime, V8 can be optimized for specific execution scenarios, thereby providing higher execution speed and lower memory usage.

To sum up, the compilation principle of V8 is to JavaScriptconvert the code into an abstract syntax tree, then optimize it, and finally generate machine code. This design can make full use of the advantages of the hardware platform and provide a high-performance JavaScriptexecution environment.

5. Code level optimization to improve V8 compilation efficiency

1. Function optimization

In order to improve the compilation efficiency of V8, it can be optimized from the following aspects:

1. Reduce function size and complexity

The size and complexity of functions have a direct impact on V8's compilation efficiency. Functions with concise code, small function size, and fewer nested call layers can reduce the time for parsing and generating bytecode.

  1. Reduce function size:

    • Remove unnecessary code : Check the function's implementation and remove unused variables, redundant code, and unnecessary control flow.
    • Split large functions : Split a large function into multiple smaller functions so that the V8 compiler can process each function faster.
    • Use inline functions : Inline short functions to where they are called to reduce the overhead of function calls.
  2. Reduce function complexity:

    • Reduce loop nesting : Avoid multi-level nested loop structures, which can be simplified by using recursion or refactoring algorithms.
    • Extract common calculations : Extract repeated calculations to reduce the execution time of repeated work.
    • Reduce the properties and methods of the object : try to streamline and merge the properties and methods of the object to avoid unnecessary complexity.

2. Avoid using dynamic features

When compiling, V8 will try its best to perform inline optimization, that is, replace the place where the function is called with the implementation code of the called function, thereby reducing the overhead of function calls. However, if the function has dynamic characteristics, such as dynamically generating functions through eval() or Function(), V8 cannot perform inline optimization, resulting in reduced compilation efficiency.

In order to improve the compilation efficiency of V8, we can reduce the compilation time by avoiding the use of dynamic features. Dynamic features refer to the use of dynamic types, dynamic parsing of variable names, dynamic binding, etc. in the code. During compilation, V8 cannot resolve these dynamic features ahead of time, thus increasing compilation time.

The following is a practical application case to illustrate how to improve the compilation efficiency of V8 by avoiding the use of dynamic features.

Suppose we have a JavaScript application that contains a dynamic string concatenation function. The input of this function is an object array, and the property names and property values ​​of the objects may be dynamic. The function of the function is to concatenate the attribute names and attribute values ​​in the object array into a string and return it.

function concatProperty(objArray) {
    
    
  let result = "";
  for (let obj of objArray) {
    
    
    for (let key in obj) {
    
    
      result += key + ": " + obj[key] + ", ";
    }
  }
  return result;
}

let objArray = [
  {
    
     name: "Alice", age: 25 },
  {
    
     name: "Bob", age: 30 },
  {
    
     name: "Charlie", age: 35 }
];

console.log(concatProperty(objArray));

In this example, we use dynamic property names and property values, so that V8 cannot determine the types of property names and property values ​​at compile time. This will cause V8 to perform dynamic analysis and type inference at runtime, which affects the compilation speed.

In order to improve the compilation efficiency of V8, we can use some optimization techniques. First, we can try to avoid using dynamic property names and property values. In the above example, if we know that the types of attribute names and attribute values ​​are fixed, we can use static attribute names and attribute values ​​instead of dynamically obtaining attribute names and attribute values ​​from objects. In this way, V8 can infer the type of the attribute name and attribute value at compile time, reducing the dynamic analysis at runtime.

The modified code is as follows:

function concatProperty(objArray) {
    
    
  let result = "";
  for (let obj of objArray) {
    
    
    result += obj.name + ": " + obj.age + ", ";
  }
  return result;
}

let objArray = [
  {
    
     name: "Alice", age: 25 },
  {
    
     name: "Bob", age: 30 },
  {
    
     name: "Charlie", age: 35 }
];

console.log(concatProperty(objArray));

By avoiding the use of dynamic features, we can improve the compilation efficiency of V8, reduce dynamic analysis and type inference at runtime, and thus speed up code execution. This is especially important for large and complex JavaScript applications.

3. Avoid eval() and with statements

The eval() and with statements will cause changes in the scope chain during the compilation phase, making it difficult for V8 to perform static analysis and optimization. Avoid using these statements as much as possible to improve compilation efficiency.

  1. Avoid using eval(): The eval() function can execute the incoming string as JavaScript code, but this will cause V8 to fail to compile and optimize ahead of time, thus affecting performance. Consider using other alternatives, such as function calls, conditional statements, etc., to avoid dynamically executing code using eval().

  2. Avoid using the with statement: the with statement creates a new scope and affects V8's optimization capabilities. It is recommended to use conventional variable declaration and access methods to ensure code readability and performance.

The following is a practical application case to illustrate:

Consider the following code fragment, which uses the eval() function and the with statement to perform dynamic property access:

function getProperty(obj, prop) {
    
    
  with(obj) {
    
    
    return eval(prop);
  }
}

const obj = {
    
     foo: 10, bar: 20 };
const property = getProperty(obj, "foo");
console.log(property);

In order to improve compilation efficiency, the code can be rewritten to avoid using eval() and with statements, as follows:

function getProperty(obj, prop) {
    
    
  return obj[prop];
}

const obj = {
    
     foo: 10, bar: 20 };
const property = getProperty(obj, "foo");
console.log(property);

By directly accessing the properties of objects without using eval() and with statements, V8 can compile and optimize the code ahead of time, thus improving performance. This way is also much clearer and easier to understand.

4. Use strict mode

In strict mode, V8 can perform more static analysis and optimization, thereby improving compilation efficiency. Strict mode is enabled with the "use strict" directive.
Using strict mode can improve the compilation efficiency of V8, mainly because the code in strict mode is more standardized and concise, eliminating some unnecessary checks and conversions, thereby reducing the workload of V8 and improving the compilation speed.

The following is a practical application case to illustrate how to improve the compilation efficiency of V8 by using strict mode.

Suppose we have a JavaScript file with the following content:

"use strict";

const name = "John";
let age = 30;

function sayHello() {
    
    
  console.log("Hello, " + name + "! You are " + age + " years old.");
}

sayHello();

In the above code, we use strict mode ("use strict"), declare two variables name and age, and define a function sayHello to print out the greeting.

Compared with code in non-strict mode, using strict mode has the following advantages:

  1. Early identification of errors: In strict mode, undeclared variables cannot be declared, otherwise a ReferenceError will be thrown. This enables developers to find errors earlier in the code and reduce debugging time.

  2. Improve operating efficiency: In strict mode, the JavaScript engine can better optimize the code. For example, the eval function and the with statement are prohibited in strict mode, and these features often lead to performance degradation. By disabling these features, the V8 engine can compile and optimize more efficiently, increasing execution speed.

By using strict mode, the V8 compiler can reduce some extra checks and conversion operations during compilation, thereby improving compilation speed. Although in this case, the impact of strict mode on compile time may not be very noticeable, for large applications, using strict mode can improve overall performance.

5. Avoid frequent function calls and closures

Frequent function calls and the use of closures increase the overhead of parsing and generating bytecode.
To improve the compilation efficiency of V8 by avoiding frequent function calls and closures, the following aspects can be considered:

  1. Reduce function calls: Avoid function calls in loop bodies and frequently executed code blocks. Inline the logic of functions that need to be called frequently to the call site to reduce the overhead of function calls.

  2. Avoid creating closures: Try to avoid creating anonymous functions or closures in loop bodies and frequently executed code blocks. The creation of closures will cause additional memory allocation and garbage collection overhead, affecting performance.

The following is a practical application case to show how to improve the compilation efficiency of V8 by avoiding frequent function calls and closures.

Suppose there is an array arr, all the elements in it need to be squared, and the squared results are accumulated. A traditional implementation might use a map()function and a reduce()function to do this:

const arr = [1, 2, 3, 4, 5];

const squaredSum = arr
  .map((n) => n * n)
  .reduce((sum, n) => sum + n, 0);

console.log(squaredSum);

In the above code, map()the and reduce()functions are frequently called, and map()closures are used in the functions, which will lead to performance loss.

map()To improve performance, the logic of sum functions can be reduce()inlined at the call site and closures can be avoided. The specific method is as follows:

const arr = [1, 2, 3, 4, 5];

let squaredSum = 0;
for (let i = 0; i < arr.length; i++) {
    
    
  const n = arr[i];
  squaredSum += n * n;
}

console.log(squaredSum);

In the above code, we have used a forloop instead of the sum map()function reduce(). Through such improvements, frequent function calls and closure creation are avoided, and the compilation efficiency of V8 is improved.

It should be noted that the above method is just a simple example, and specific optimization methods should be selected and applied according to actual needs and application scenarios. You can also use the tools and methods provided by V8, such as analyzing --trace-optthe optimization of V8 to optimize the performance of JavaScript code.

6. Use sufficient type information

V8 supports JIT (Just-In-Time) compilation, which can be dynamically compiled according to the type information at runtime. In places such as function parameters and return values, try to use specific types instead of generic types, which can improve compilation efficiency.

By providing sufficient type information for V8, its compilation efficiency can be improved. This is because V8 is a JavaScript engine based on a JIT (Just-in-Time) compiler, which compiles JavaScript code into efficient machine code at runtime. Type information helps V8 make more accurate compilation decisions and optimize the generated machine code.

A practical application case is to use TypedArray to handle a large number of numerical calculations. TypedArray is a special array type in JavaScript that allows us to directly manipulate binary data in memory to achieve high-performance numerical calculations.

Suppose we have an application that needs to perform some numerical operations on an array of one million elements, such as sum, average, etc. We can use TypedArray to define the type of this array and tell V8 about the type information of each element.

Here is a sample code using TypedArray:

// 创建一个包含一百万个元素的Float64Array
const array = new Float64Array(1000000);

// 填充数组
for (let i = 0; i < 1000000; i++) {
    
    
  array[i] = Math.random();
}

// 计算数组的总和
let sum = 0;
for (let i = 0; i < 1000000; i++) {
    
    
  sum += array[i];
}

console.log(sum);

In this example, we use the Float64Array type to define the type of the array, telling V8 that the array contains 64-bit floating point numbers. By providing type information, V8 can optimize the code at compile time and generate more efficient machine code.

To sum up, by using sufficient type information, such as TypedArray to define the type of variables, it can help V8 to make more accurate compilation decisions and generate more efficient machine code, thereby improving the compilation efficiency of V8.

In summary, V8 compilation can be improved by optimizing function size and complexity, avoiding dynamic features, avoiding eval() and with statements, using strict mode, avoiding frequent function calls and closures, and using sufficient type information efficiency.

2. Object optimization

V8 is a high-performance JavaScript engine that compiles JavaScript code into machine code for execution. In V8, the operation of adding, deleting, modifying and querying objects will involve operations such as object attribute access, attribute insertion or deletion, and attribute modification.

V8 uses mechanisms such as hidden class (Hidden Class) and inline cache (Inline Cache) to improve the access efficiency of objects. A hidden class is a compile-time binding mechanism for object properties and corresponding values, which is used to track the object's shape and property layout. When the properties of the object change, it will judge whether the hidden class needs to be updated according to the information of the hidden class, thereby reducing the modification cost of the object property. Inline caching is a caching mechanism used to cache the access paths of attributes. Through inline caching, V8 can reduce the lookup cost of attribute access and speed up access.

To improve the compilation efficiency of V8, we can follow the following points:

  1. Avoid frequent attribute insertion or deletion: Frequent modification of object attributes will cause changes in hidden classes and affect performance. If you need to modify attributes frequently, you can consider using data structures such as arrays, or encapsulate attributes into internal variables.

  2. Avoid using dynamic property names: Using dynamic property names makes it difficult for V8 to optimize object access. If dynamic property names are required, consider storing the properties in a Map object.

  3. Avoid frequent property access: Frequently accessing an object's properties can also affect performance. If a property is accessed frequently, it can be stored in a local variable to reduce the cost of property lookups.

  4. Avoid modifying the same attribute multiple times: Modifying the same attribute multiple times will result in multiple hidden class changes and affect performance. If you need to modify attributes multiple times, you can combine modification operations into one modification.

Here is a simple example to illustrate:

// 不推荐的写法
function updateObject(obj, prop, value) {
    
    
  if (!obj.hasOwnProperty(prop)) {
    
    
    obj[prop] = value;
  } else {
    
    
    obj[prop] += value;
  }
}

// 推荐的写法
function updateObject(obj, prop, value) {
    
    
  const oldValue = obj[prop] || 0;
  obj[prop] = oldValue + value;
}

In the not-recommended way of writing, every time you modify an object's attribute, you need to first determine whether the attribute exists, and then perform the corresponding operation, which will lead to frequent attribute access and modification. In the recommended writing method, we first store the attribute value in a local variable, and then modify it, avoiding the cost of multiple attribute access and modification, thereby improving the compilation efficiency of V8.

In a word, the compilation efficiency of V8 can be improved by rationally manipulating the attributes of objects and avoiding frequent attribute operations.

Summarize

In front-end performance optimization, JavaScript code optimization is a very important part. Reasonable code writing, modular development, asynchronous loading, compression and caching can effectively improve page response speed and user experience. We need to pay attention to indicators such as JavaScript file size, execution time, and number of requests in the page, and optimize them in a targeted manner. At the same time, it is also necessary to pay attention to both user experience and code maintainability, so as to improve performance without affecting code expansion and maintenance. I hope this article can help readers better understand and apply JavaScript optimization methods, and improve their front-end technical level.

Guess you like

Origin blog.csdn.net/jieyucx/article/details/132489433