JavaScript performance optimization (performance and memory management)

Article description: This article is the notes and experience of the front-end training camp of Regao. If there is something wrong, I hope you can point out and teach, thank you! 

1. Performance

Why use Performance

  • The purpose of GC is to achieve a virtuous cycle of memory space
  • The cornerstone of a virtuous circle is rational use
  • Always pay attention to determine whether it is reasonable
  • Performance provides multiple monitoring methods

Performance steps

  • Open the browser and enter the destination URL
  • Open the developer tools panel, select performance
  • Turn on the recording function and visit the specific page
  • Perform user behavior, record after a period of time, then end the recording, click the performance button to view the memory usage

Second, the manifestation of memory problems

External manifestations of memory problems:

  • Lazy loading or frequent pauses in the page (the network environment is normal)
  • Page persistent bad performance
  • The performance of the page gets worse and worse over time

Third, the centralized way of monitoring memory

Standards for defining memory problems

  • Memory leak: memory usage continues to rise
  • Memory expansion: There are performance problems on most devices (the current application itself needs a lot of memory space in order to achieve the best results. In this process, perhaps the current device itself does not support the hardware, then (Causes the difference in performance during use)
  • Frequent garbage collection: analysis through the memory change graph

Several ways to monitor memory

  • Browser task manager
  • Timeline sequence chart record
  • Heap block photos to find and separate DOM
  • Determine whether there is a cumbersome garbage collection

 1. Task Manager monitors memory

Use the shortcut key shift+esc to call up the browser task manager, right-click to select the JavaScript memory of the current page and it will be displayed

The memory occupied by the first column is native memory. A simple understanding is that there are many Dom nodes in the current interface. This memory is the memory occupied by the Dom. If the memory continues to increase, it means that the page continues to create new Doms.

The last column of memory used by JavaScript indicates the heap of js. What we need to pay attention to is the value in parentheses. It indicates the size of all reachable objects. If this number is increasing, then the current Either new objects are being created in the interface, or the current existing objects are constantly growing.

You can only find out if there are any problems, and positioning problems is not easy to use.

2.Timeline recording memory

We use Timeline to record memory changes, which can observe memory changes more accurately. Examples:

 

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
    <title>任务管理器监控内存变化</title> 
</head> 
<body>
    <button id="btn">
        Add
    </button>
<script type="text/javascript">
    const arrList = []

    function test(){
        for(let i = 0;i < 100000;i++){
            document.body.appendChild(document.createElement('p'))
        }
        arrList.push(new Array(100000).join('x'))
    }
    document.getElementById('btn').addEventListener('click',test)
</script> 
</body> 
</html>

Open the html file with the chrome browser, open the console, click the performance option, start recording, and then click the add button on the page, click several times intermittently, and then end the screen recording.

 

 3. Heap snapshot search and separate DOM

What is separate DOM

  • Interface elements live on the DOM tree
  • The DOM node of the garbage object: If this node is detached from the current DOM tree, and there is no DOM node referenced by anyone in the js code, it will actually become a garbage
  • DOM node in separated state: If this node is detached from the current DOM tree, and there is a DOM node referenced by someone in the js code, it is called a DOM node in separated state

Example: After clicking the button, a separated DOM is generated in the DOM tree. The separated DOM is not used on the page, but is used in the code, which causes a waste of space. We leave TmpEle empty and let the GC collect garbage.

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
    <title>任务管理器监控内存变化</title> 
</head> 
<body>
    <button id="btn">
        Add
    </button>
<script type="text/javascript">
    var tmpEle

    function  fn() {
        var ul = document.createElement('ul')
        for(var i = 0; i<10 ;i++ ){
            var li = document.createElement('li')
            ul.appendChild(li)
        }
        tmpEle = ul
    }

    document.getElementById('btn').addEventListener('click',fn)//js中创建的节点并没有往我们页面里添加,是分离的DOM
</script> 
</body> 
</html>

Insert picture description here

 The source of the above animation: https://coder5.blog.csdn.net/article/details/109723747

4. Determine whether there is frequent GC

Why determine frequent garbage collection

  • The application is stopped while the GC is working
  • Frequent and long GC will cause application to die
  • Users perceive application freezes during use

Determine frequent garbage collection

  • Frequent rise and fall in the Timeline
  • Frequent increase and decrease of data in task manager

Four, code optimization introduction

How to accurately test JavaScript performance

  • Essentially, collecting a large number of execution samples for mathematical statistics and analysis
  • Use https://jsperf.com based on Benchmark.js (the server is out of maintenance and is no longer used, you can use JSBench: https://jsbench.me/ ) to complete

Jsperf use process

  • Log in with GitHub account
  • Fill in personal information (not required)
  • Fill in detailed test case information (title, slug): slug must be unique
  • Fill in the preparation code (usually used in DOM operations)
  • Fill in the necessary setup and teardown codes: setup is a pre-preparation, teardown is a work to be destroyed after all codes are executed
  • Fill in the test code snippet
  • Insert picture description here

Five, use global variables with caution

Why use caution

  • Global variables are defined in the global execution context and are the top of all scope chains
  • The global execution context always exists in the context execution stack, and will not disappear until the program exits, which is not good for the working state of the GC, because the GC will not reclaim the global variable as long as it finds that the global variable is still alive.
  • If a variable with the same name appears in a local scope, it will obscure or pollute the global

Code demo:

var i,str = ''
for(i = 0;i<1000;i++){
    str += i
}

for(i = 0;i<1000;i++){
    let str = '' //函数内部定义的局部变量
    str += i
}

 https://jsperf.com demo:

 

 It can be seen that the performance of the code of i defined by global variables is not as good as that defined in local variables.

Cache global variables:

Cache the unavoidable global variables in use locally. When we want to find DOM elements, in this case, we must use document. This document is not defined by us later, but exists in our current global The object is directly built-in and can be used, so in this case, you can choose to place such a large number of global variables that need to be reused in a local action, so as to achieve the effect of caching.

Code demo:

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" charset="UTF-8" />
    <title>缓存全局变量</title> 
</head> 
<body>
    <input type="button" value="btn" id="btn1">
    <input type="button" value="btn" id="btn2">
    <input type="button" value="btn" id="btn3">
    <input type="button" value="btn" id="btn4">
    <p>1111</p>
    <input type="button" value="btn" id="btn5">
    <input type="button" value="btn" id="btn6">
    <p>222</p>
    <input type="button" value="btn" id="btn7">
    <input type="button" value="btn" id="btn8">
    <p>333</p>
    <input type="button" value="btn" id="btn9">
    <input type="button" value="btn" id="btn10">
<script type="text/javascript">
    function getBtn(){
        let oBtn1 = document.getElementById('btn1')
        let oBtn3 = document.getElementById('btn3')
        let oBtn5 = document.getElementById('btn5')
        let oBtn7 = document.getElementById('btn7')
        let oBtn9 = document.getElementById('btn9')
    }

    function getBtn2(){
        let obj = document
        let oBtn1 = obj.getElementById('btn1')
        let oBtn3 = obj.getElementById('btn3')
        let oBtn5 = obj.getElementById('btn5')
        let oBtn7 = obj.getElementById('btn7')
        let oBtn9 = obj.getElementById('btn9')
    }
</script> 
</body> 
</html>

Put the html code in the body into the preparation phase, without any content other than the body

Code that caches global variables has higher performance

Add additional methods through the prototype object: Add the methods required by the instance object on the prototype object. The code is demonstrated as follows:

var fn1 = function(){
    this.foo = function(){//相当于在某个构造函数内部直接给所有的对象实例添加上了成员方法,后续再使用的过程中都可以拿到
        console.log(11111)
    }
}
let f1 = new fn1()

var fn2 = function(){}
fn2.prototype.foo = function(){//通过原型对象添加方法
    console.log(11111)
}

let f2 = new fn2()

 

 It is better to add methods through prototype objects.

 

Guess you like

Origin blog.csdn.net/weixin_41962912/article/details/110135353