Use global variables with caution
-
When declaring variables, we should try to use the block level domain variable declaration keyword to declare variables, and use the global scope as little as possible. The global variable is defined in the global execution context, and it is also the top of all scopes. If we define a variable as a global variable, JavaScript will scan up from the block level until the top to find the variable, so the search time consumption will become larger.
-
Global variables will always exist in the context execution stack until the program exits. This will also cause global variables to never be recycled as garbage and will always occupy memory space.
-
If a variable with the same name appears in a local scope, it will mask or pollute the global scope
Therefore, if we use global variables, we have to consider more situations, and it will always bring us unexpected problems.
- Localizing variables: localizing some variables can improve the execution efficiency of code (reducing the paths to be found during data access, that is, the optimization of scope chain search);
Data storage and reading: for data storage and reading, it is better to reduce its access level in the search of the scope chain, so as to improve the execution speed of the code. Therefore, if circumstances permit, it is recommended to directly place the variable data in the peer scope of the current execution context;
var i,str = ''; function packageDom(){ for(i=0;i<1000;i++){ str+=i }; console.log(str); }; packageDom() //After variable localization function packageDom(){ let str = ''; for(let i=0;i<1000;i++){ str+=i }; console.log(str) } packageDom()
Cache data
The data that needs to be used many times shall be saved in advance and used later. (the purpose is to reduce the level of access to find it and minimize the path)
We can cache global variables that cannot be avoided in use to the local scope. The code is as follows:
var oBox = document.getElementById('btn') function hasClassName(ele,cls){ console.log(ele.className) console.log(ele.className) console.log(ele.className) console.log(ele.className) } hasClassName(oBox,btn); function hasClassName(ele,cls){ const name = ele.className console.log(name) console.log(name) console.log(name) console.log(name) } hasClassName(oBox,btn);
In short, the purpose of variable localization and caching data is: when accessing data, try to put it where it can be found as soon as possible without hiding it too deep, which can reduce the time consumed in reading.
Anti shake and throttling
Why anti shake and throttling are needed:
- When some high-frequency events are triggered, we do not want the corresponding event handler to execute multiple times
- Scenario:
- Scroll event
- High frequency click of the rotation chart switching button
- input keyboard event triggers fuzzy query
- By default, the browser will have its own time interval for monitoring events (Google 4~6ms). If the events are monitored repeatedly and executed frequently, it will cause unnecessary waste of resources
**Anti shake: * * for high-frequency operations, we only want to identify one click, which can be the first or last.
**Throttling: * * for high-frequency operation, do not let it be executed only once; Instead, we can set the frequency ourselves to trigger events that would have been triggered many times, and reduce the number of triggers according to the frequency flow defined by us.
Implementation of anti shake function
An anti shake function is preliminarily realized:
const btn = document.getElementById('btn'); /* *handle Callback function to be executed finally *wait waiting time *immedite Controls whether the first or last execution is performed. false is the last execution */ function MyDebonce(handle,wait,immedite){ // Parameter type and default value processing if( typeof handle !== 'function' ) throw new Error('handle must be an function !') if( typeof wait === 'boolean' ) { immedite = wait wait = 500 } if(!wait) wait = 500 if( typeof immedite !== 'boolean' ){ immedite = false; } let timer = null; // The so-called "anti shake" means that there is a "person" to manage the execution times of the handle function; // Click on the event. this point and the event object always need to be restored. Anonymous functions receive the parameters of the click object return function(...args){ let init = immedite && timer==null; let self = this; clearTimeout(timer) timer = setTimeout( ()=>{ timer = null !immedite ? handle.call(self,...args) : null; },wait ); init?handle.call(self,...args):null } } // Define event execution functions function btnClick(event){ console.log('Click!',this,event) } btn.onclick = MyDebonce( btnClick,500,true )
It can be seen that the mydebounce function is executed only once, and the btnClick callback function is passed into the anti shake function mydebounce as a parameter together with other parameters, and then a closure is formed. This is also an application of function coritization.
throttle
Throttling: throttling here means that events are triggered only once in a user-defined period of time;
Core operation: execute the proxy function. When the onscroll event is executed, instead of executing the scrollfn, execute its proxy function, and then control the execution frequency of scrollfn in the proxy function proxy.
const btn = document.getElementById('btn'); function MyThrottle(handle,wait){ // Parameter type and default value processing if( typeof handle !== 'function' ) throw new Error('handle must be an function !') if( typeof wait === 'boolean' ) { immedite = wait wait = 500 } if(!wait) wait = 500; if( typeof immedite !== 'boolean' ){ immedite = false; }; let previous = 0; let timer = null; return function proxy(e){ let now = new Date(); let interval = wait - (now - previous); if( interval<=0 ){ // This indicates that it is a non high frequency operation, and the handle can be executed previous = now; handle.call(this,e,"interval<=0"); }else if( !timer ){ // If there is a time delay device in the system, it does not need to be turned on again // This indicates that this operation is not the first time to call proxy within the frequency defined by us, and should not execute handle // At this time, you can define a delayer to let the handle execute after the interval timer = setTimeout(()=>{ // clearTimeout(timer) timer = null; previous = new Date(); handle.call(this,e,"interval>0"); },interval) } } } function handle(e,aa){ console.log("Click!",this,aa) } btn.onclick = MyThrottle(handle,500);
Reduce if judgment level
In our projects, there are often situations of judging the nesting of conditions. We can return useless and invalid conditions in advance to achieve the effect of nesting optimization.
// Before optimization function doSomething (part, chapter) { const parts = ['ES2016', 'Engineering', 'Vue', 'React', 'Node'] if (part){ if(parts.includes(part)){ console.log('Belongs to the current course') if(chapter > 5){ console.log('You need to provide VIP identity') } } }else{ console.log('Please confirm the module information') } } // After optimization function doSomething (part, chapter) { const parts = ['ES2016', 'Engineering', 'Vue', 'React', 'Node'] if (!part) return console.log('Please confirm the module information') if(!parts.includes(part)) return console.log('Belongs to the current course') if(chapter > 5){ console.log('You need to provide VIP identity') } }
Summary:
- Whenever there are multiple nested if else, you can consider whether you can reduce the judgment level by return ing invalid code in advance;
- if else is generally used for interval condition judgment,
- If it is clear that there are several enumeration values, it is more recommended to use switch case, so that the code will be clearer and easy to maintain.
- Of course, easy to maintain code does not necessarily execute fast.
Reduce circulatory activity
The more things you do in the loop body, the slower the loop. Therefore, we can optimize some code that does not need to be placed in the loop by placing it outside the loop body. Extract the data values that need to be used or operated in each cycle from the outside of the loop body (similar to data cache);
// Before optimization var test = () => { let i; let arr = ['zce', 18, 'I live for the front'] for (i = 0; i < arr.length; i++) { console.log(arr[i]) } } // After optimization, it is actually the basic for loop optimization, and then reduce unnecessary logic in the loop var test = () => { let i; let arr = ['zce', 18, 'I live for the front'] const len = arr.length for (i = 0; i < len; i++) { console.log(arr[i]) } }
The idea of traversing from back to front will make less conditional judgment
var test = () => { let arr = ['zce', 18, 'I live for the front']; const len = arr.length while(len--){ console.log(arr[len]) } }
Select the optimal circulation mode
var arrList = new Array(1, 2, 3, 4, 5)
forEach loop
arrList.forEach(item => { console.log(item) })
for loop
var len = arrList.length; for (let i = 0; i<len; i++) { console.log(arrList[i]) }
for of loop
var i; for ( i of arrList ) { console.log(arrList[i]) }
After comparison, it is found that the execution speed of forEach loop is the best, decreasing in turn. And the speed of foeEach is much better than other cycles!
Literal quantity and construction formula
Reference type data: literal creation compared with constructor creation
Creating objects by literal is much faster than by constructors.
// Before optimization let test = () => { let obj = new Object() obj.name = 'mn' obj.age = 25 obj.slogan = 'I live for the front' return obj } // After optimization, use literal instead of constructor let test = () => { let obj = { name: 'mn', age: 25, slogan: 'I live for the front' } return obj } console.log(test())
Because when creating an object through the new keyword, it involves the call of a function, doing more things and consuming more time;
Creating a literal object is like directly opening up a space and storing things in it.
Basic data type:
var str1 = 'I'm a string' var str2 = new String('I'm a string')
After testing in JSbench, it is found that their differences are very obvious. The difference between reference types is less than 5%, while the difference between string types is very large (more than 90%);
str1 is a string and str2 is an Object. When it is created, it needs extra space to store some properties and methods on the Object.
Optimizing dom node addition
dom operations are very frequent and the performance consumption is very serious. Adding nodes to the dom will inevitably lead to backflow and redrawing. Therefore, we should avoid frequent direct operations on the dom to optimize the performance loss. An example is as follows:
// Before optimization for (let i = 0; i < 10; i++) { let oP = document.createElement('p') oP.innerHTML = i document.body.appendChild(oP) } // After optimization, we only need to directly operate the document once, avoiding multiple dom changes and redrawing const fragEle = document.createDocumentFragment() for (let i = 0; i < 10; i++) { let oP = document.createElement('p') oP.innerHTML = i fragEle.appendChild(oP) } document.body.appendChild(fragEle)
Optimize dom node operations
When we add a node with the same label and style but different from the text of the existing elements in the document to the document, we do not need to create a new label and style node and insert it again. Instead, we can create a node by copying and cloning (it will take up less memory faster) and add it to the document.
// Before optimization for (let i = 0; i < 3; i++) { let oP = document.createElement('p') oP.innerHTML = i document.body.appendChild(oP) } // After optimization const oldP = document.getElement('box1') for (let i = 0; i < 3; i++) { let newP = oldP.cloneNode(false) newP.innerHTML = i document.body.appendChild(newP) }