Author: Mai Le

Source: Hang Seng LIGHT Cloud Community

I. Data Access

Recognize scope chains

function add(num1, num2) {
    var sum =num1 + num2
    return  sum
}
Copy the code

When the Add function is created, its scope chain is filled with a single mutable object that contains all the variables accessible to the global scope.

When the add function executes, an internal object called the runtime context is created. This object is pushed to the top of the scope chain.

Performance of identifier resolution

When a variable is accessed, the query starts at the top of the scope chain (0). If it is found, it is used. If it is not found, 0 continues to look for (1) along the scope chain. In real time, lookups consume performance, and the deeper an identifier is, the slower it will read and write. Therefore, local variables are always the fastest to read and write in a function, and global variables are always the slowest. The test for each browser is shown below:

function initUi() {
  var bd = document.body,
      links = document.getElementByName('a'),
      i = 0,
      len = links.length;
  while(i < len) {
    // do something
    i++;
  }
  document.getElementById('btn').onclick = function() {
    // do something}}Copy the code

This function accesses Document three times, and document is a global variable, and each time it accesses it, it must traverse the entire scope chain until it finds the variable. Performance costs can be reduced by:

function initUi() {
  var doc = document   
      bd = doc.body,
      links = doc.getElementByName('a'),
      i = 0,
      len = links.length;
  while(i < len) {
    // do something
    i++;
  }
  doc.getElementById('btn').onclick = function() {
    // do something}}Copy the code

Change the scope chain

With and try catch can change the scope chain of a run-time context.

Rewrite the initUi function with:

function initUi() {
    with(document) {
      var bd = body,
          links = getElementByName('a'),
          i = 0,
          len = links.length;
      while(i < len) {
        // do something
        i++;
      }
      getElementById('btn').onclick = function() {
        // do something}}}Copy the code

Using with also avoids the problem of repeatedly accessing a global variable, which appears more efficient and actually creates a performance problem. When the with statement is run, the scope chain of the runtime context is changed:

This means that all local variables are now in the second scoped chain object, making access more expensive.

The same is true with try-catch. When something goes wrong with a try, we jump straight to the catch, and then we push the exception object to the head of the scope chain, and all of the local variables in the try are in the second place. Once the catch is complete, the scope chain returns to its previous state. If you use a try-catch, try not to access local variables in a catch. Handle exceptions as follows:

try {
// do
} catch (error) {
  handleError(error)
}
Copy the code

Dynamic scope

With try-catch eval is considered a dynamic scope. Optimized JavaScript engines that try to analyze the code to determine which variables can be accessed at a particular time. These engines attempt to bypass traditional scoped chain lookups in favor of quick lookups in the form of identifier indexes. This kind of optimization fails when it comes to dynamic scope. The script engine must switch back to slower hash table-based identifier recognition, which is more like traditional scope-chain look-up.

function execute(str) {
    eval(str)
    console.log(window)
}
execute('var window = {}')
Copy the code

Closures, scope, and memory

function assignEvents() {
  var id = "12"
  document.getElementById('btn').onclick = function() {
    saveDocument(id)
  }
}
Copy the code

The above function creates a closure with a different chain of scopes than normal functions:

Normally, the execution context object of a function is destroyed along with the run-time context, and because the closure’s [[scope]] property references the execution context object, it is not destroyed. This means that closures require more memory overhead.

When the closure is executed, a runtime context is created, its scoped chain is initialized at the same time as the two same scoped chain objects referenced in the attribute [[Scope]], and then an active object is created for the closure itself.

The two identifiers used in the closure, ID and saveDocument, are at levels 1 and 2 of the scoped chain, and there is a high performance overhead if a large number of identifiers are accessed across the scoped chain.

When using closures, you can also mitigate the impact by turning global variables into local variables.

When the same external variable is accessed frequently in a function, try to make it local. Try not to use with eval. Try to avoid access to external variables (both local and global) when using a try-catch. Be careful with closures and pay attention to the depth of access of identifiers.

Two DOM programming

Dom access and modification

Dom access and modification is always expensive, so be careful when using it. For example:

function innerHTMLLoop() {
  for(var i = 0; i < 15000; i++) {
    document.getElementById('here').innerHTML += 'a'}}Copy the code

Use Chrome to test:

console.time()
innerHTMLLoop()
console.timeEnd()
/ / the default: 820.547119140625 ms
Copy the code

The problem with this code is that for each iteration of the loop, the element is accessed twice: once to read the innerHTML attribute value and once to override it. Can be optimized as:

function innerHTMLLoop() {
  var count = ' '
  for(var i = 0; i < 15000; i++) {
    count += 'a'
  }
  document.getElementById('here').innerHTML += count;
}
Copy the code
console.time()
innerHTMLLoop()
console.timeEnd()
/ / the default: 1.85595703125 ms
Copy the code

The execution time increased from about 800ms to about 2ms.

Expensive set of elements

This code appears to simply double the number of div elements on the page. It iterates through the existing div elements, creating a new div each time and adding it to the body. This is actually an infinite loop, because the exit condition for the loop, Alldivs.length, increases with each iteration and reflects the current state of the underlying document.

var allDivs = document.getElementsByTagName('div')
    for(var i = 0; i < allDivs.length; i ++) {
    document.body.appendChild(document.createElement('div'))}Copy the code

It is not recommended to read the length property of the array in the conditional control statement of the loop. Reading the length of a collection is much slower than reading the length of an ordinary array because you have to query again each time. When using collections, remember to convert collections to arrays, or avoid taking collections’ length too often in the body of the loop.

function toArray(coll) {
    return Array.prototype.slice.call(coll)
}
var allDivs = document.getElementsByTagName('div')
var len = allDivs.length
for(var i = 0; i < len; i ++) {
    document.body.appendChild(document.createElement('div'))}Copy the code

Finding DOM nodes

Using children instead of childNodes is faster because there are fewer collection items. The whitespace in the HTML source is actually a text node, and it is not included in the children collection.

CSS selectors

Use querySelectorAll whenever possible

The latest browsers also provide a native DOM method called querySelectorAll (). This way, of course, is much faster than using JavaScript and DOM to find elements by walking through them.

var elements=document.querySelectorAll (' #menu a ');Copy the code

The return value is an array of classes, but not an HTML collection. The return value is like a static list, and taking length multiple times does not cause double-counting.


var allDivs = document.getElementsByTagName('div')
console.log(allDivs)
var qDivs = document.querySelectorAll('#here')
console.log(qDivs)
Copy the code

If you need to process a large number of combined queries, using querySelectorAll () is more efficient. For example, if you have div elements with a class of “warning” and other elements with a class of “notice” on the page, you are advised to use querySelectorAll () to get a list of both:

var errs=document.querySelectorAll (' div. Warning,div. Notice ');Copy the code

Minimize redraw and rearrange

Method one, merge and modify styles

The following code triggers three rearrangements and redraws.

After optimization, only trigger once:

var el=document.getelementById (' mydiv '); El. Style. CssText = 'border - left: 1 px; border-right: 2px; padding: 5px; ';Copy the code

Method two, take the DOM out of the document flow

1 Hide elements, modify them, and display them again

Add the following data to ul

var data = [
  {
    url: 'http://www.csdn.com'.text: 'blog'
  },
  {
    url: 'http://www.csdn.com'.text: 'blog'}]Copy the code
 <ul>
    <li><a href="http:www.baidu.com">baidu</a></li>
 </ul>
Copy the code

Updating the list in the normal way below is quite performance destructive because each loop causes a reorder.

function appDataToEl(ul, data) {
  var doc = document;
 
  for(let i = 0; i < data.length; i++) {
    var liE = doc.createElement('li')
    var aE = doc.createElement('a')
    aE.innerHTML = data[i].text;
    aE.href =  data[i].url
    liE.appendChild(aE)
    ul.appendChild(liE)
  }
}
appDataToEl(document.getElementById('uls'), data)
Copy the code

The optimization is as follows:

var ul = document.getElementById('uls')
ul.style.display = 'none'
appDataToEl(ul, data)
ul.style.display = 'block'
Copy the code

2. Use document fragments

var ul = document.getElementById('uls')
var frame = document.createDocumentFragment()
appDataToEl(frame, data)
ul.appendChild(frame)
Copy the code

3 Create a backup of the node to be modified, and then operate on the copy. Once the operation is complete, replace the old node with the new one.

var old = document.getElementById('uls')
var clone = old.cloneNode(true)
appDataToEl(clone, data)
old.parentNode.replaceChild(clone, old)
Copy the code

It is recommended to use document fragments whenever possible (the second option) because they produce the fewest DOM traversals and rearrangements.

Method three takes the animation out of the document flow

An animation at the top of the page that slides over the rest of the page can result in an expensive massive rearrangement that makes the user feel like the page is slapping. The more nodes in the render tree that need to be recalculated, the worse it gets.

1. Use absolute location to position animated elements on the page out of the document flow.

2. Get the elements moving. As it expands, it temporarily overwrites parts of the page. But this is only a small area of the page redrawn process, does not result in rearranging and redrawing large parts of the page.

3. Restore positioning when the animation ends, so that the rest of the document is moved down only once.

Method 4 event delegate

This can affect performance when there are a large number of elements in a page and each one is bound to an event handler once or more, such as onclick. A simple and elegant technique for handling DOM events is event delegation. It is based on the fact that events bubble layer by layer and can be captured by the parent element. With the event broker, you can handle all events that fire on its children by simply binding a handler to the outer element.

Three algorithms

cycle

For, do while, while, for in, the only one that is slower is for in, because it not only looks for the object itself, but also looks for the prototype of the object, avoiding the use of for in to loop through an array.

Reverse cycling improves performance

for(var i = arr.length; i--;) {
  console.log(arr[i])
}
var i = arr.length
while(i--) {
  console.log(arr[i])
}
var i = arr.length
do {
console.log(arr[--i])
} while(i)
Copy the code

1. Comparison in one control condition (I ==true)

2. A subtraction operation (I –)

3. An array lookup (items[I])

4. A function call (console.log (items[I]))

The new looping code reduces the number of operations by two per iteration, and the performance improvement becomes more obvious as the number of iterations increases.

To optimize the if the else

If else compare to switch

There is little difference in speed between the two. Switch is easier to read if there are more judgment conditions.

The goal of if-else optimization is to minimize the number of conditions that need to be determined before reaching the correct branch. The easiest way to optimize is to make sure that the most likely conditions are in the first place. Consider the following code:

var value = 4;
 
if(value <= 5) {}else if (value > 5 & value < 10) {}else{}Copy the code

If there is a high probability that value is less than or equal to 5, the condition that is less than 5 can be placed in the first place; otherwise, the condition with a high probability can be selected to avoid being placed in the first place.

Another way to reduce the number of conditional judgments is to organize if-else statements into a series of nested if-else statements. Using a single, bulky if-else usually results in slow execution because each condition requires judgment. Such as:

var value = 9;
 
if(value === 1) {}else if(value === 2) {}else if(value === 3) {}else if(value === 4) {}else if(value === 5) {}else if(value === 6) {}else if(value === 7) {}else if(value === 8) {}else if(value === 9) {}else{}Copy the code

In this if-else expression, the conditional statement is evaluated up to 10 times. Assuming that the values of value are evenly distributed between 0 and 10, this increases the average running time. To minimize the number of conditional judgments, the code can be rewritten as a series of nested if-else statements, such as:

var value = 9;
if(value <= 5) {
  if(value === 1) {}else if(value === 2) {}else if(value === 3) {}else if(value === 4) {}else{}}else {
  if(value === 6) {}else if(value === 7) {}else if(value === 8) {}else if(value === 9) {}else{}}Copy the code

Avoid if else and switch

var value = 3
var resultArr = [1.2.3.4.5.6.7.8.9.10]
var result = resultArr[value]
Copy the code

Using objects or arrays instead of if else improves both portability and performance.

Conclusion:

● For, while, and do-while loops have similar performance characteristics, so no one type of loop is significantly faster or slower than the others.

● Avoid for-in loops unless you need to iterate over an object with an unknown number of attributes.

● The best way to improve loop performance is to reduce the amount of computation per iteration and reduce the number of loop iterations.

● Switch is generally faster than if-else, but not always the best solution.

● Use lookup tables faster than if-else and switch when there are many criteria.

● The size of the browser call stack limits the application of recursive algorithms in JavaScript; Stack overflow errors cause other code to stop running.

● If you encounter stack overflow errors, change the method to an iterative algorithm, or use Memoization to avoid double-counting.

The greater the amount of code running, the greater the performance gain from using these policies.

Four strings and regular expressions

String linkage

+= “one” + “two”;

When this code runs, it goes through four steps:

1. Create a temporary string in memory

2. The concatenated string “onetwo” is assigned to the temporary string

3. The temporary string is concatenated with the current value of STR

4. Result assigned to STR

In the book, the above method of combining strings is compared to STR = STR + “one” + “two”; It is slower. I have tested it in Chrome and found:


function testStr() {
  var str = ' '
  for(var i = 0; i < 200000; i++) {
    str += "one" + 'two'}}function testStr1() {
  var str = ' '
  for(var i = 0; i < 200000; i++) {
    str = str + "one" + 'two'}}console.time()
testStr()
console.timeEnd()
/ / the default: 14.6337890625 ms
console.time()
testStr1()
console.timeEnd()
/ / the default: 26.135009765625 ms
Copy the code

The result is the opposite, presumably because modern browsers have optimized the concatenation method.

In FireFox, the time difference is not significant.

Internet Explorer is also the first to be slightly faster than the one below.

Compared to the first method, concatenating strings using arrays, the optimization is not obvious, but in Internet Explorer, array methods are time-consuming.

function arrToStr() {
  var str = ' ', arr = []
  for(var i = 0; i < 200000; i++) {
    arr.push("one" + 'two')
  }
  str += arr.join(' ')}Copy the code

STR’s contact method is slightly slower.

Regular expression optimization

The key to regular expression optimization is to understand how it works and understand backtracking. Backtracking can be expensive to calculate, and can get out of control if you’re not careful.

Concept of backtracking

The basic idea is: from the problems of a particular state (initial state), the search from this state Can achieve all of the “state”, when a road to the “end” (can’t), then take a step back or a number of steps, starting from another possibility “state”, continue to search until all of the “path” (state) are tested. This method of constantly “moving forward” and “backtracking” to find a solution is called “backtracking”.

A scene that causes backtracking

  • Greed quantifiers

There is no backtracking

Suppose our re is /ab{1,3}c/, and when the target string is “abbbc”, there is no “backtracking”. The matching process is as follows:

There is backtracking

If the target string is “abbc”, there is a backtrace.

Step 7 and step 10 are backtracking. Step 7 is the same as step 4, in which b{1,3} matches two “b”, and step 10 is the same as step 3, in which b{1,3} matches only one “b”, which is the final result of b{1,3}.

Lazy quantifiers

var string = "12345"; 
var regex = / (\ d {1, 3}? / (\ d {1, 3}); 
console.log( string.match(regex) ); 
// => ["1234", "1", "234", index: 0, input: "12345"]
Copy the code

The target string is “12345” and the matching procedure is:

Know you are not greedy, very satisfied, but in order to match the whole, there is no way, can only give you a little more. So finally \d{1,3}? The matching character is “12”, which is two numbers, not one.

Branching structure

Branching is also lazy, but can cause backtracking

var reg1 = /^can|candy$/g
console.log('candy'.match(reg1)) // [ 'can' ]
 
var reg = / ^ (? :can|candy)$/g
console.log('candy'.match(reg)) // [ 'candy' ]
Copy the code

The matching process for the second re above:

Conclusion:

  • Greedy quantifier “try” strategy is: buy clothes bargain. The price is too high. Make it cheaper. No, make it cheaper.
  • The strategy of inert quantifier “try” is to sell things at a higher price. Less, more, okay? A little less, more.
  • The strategy for branching out is to shop around. Not this one. Go to another one. Not yet.

Comparing the backtracking of greedy quantifiers and lazy quantifiers:

Lazy quantifiers match more often than greedy quantifiers, and it’s hard to see which one is more efficient, even if the matching content is long, in different browsers.

When regular expressions cause your browser to freeze for seconds, minutes, or even longer, the problem is most likely due to runaway backtracking.

Solution: To be added

V. Response speed

Browser responsiveness is an important performance concern. Most browsers allow a single thread to execute javascript code and user interface updates, meaning that if javascript code takes too long to execute, the user interface will not be updated in a timely manner, or even new tasks will not be queued into the UI.

Browser UI thread

The browser UI thread does two things, executing javascript and updating the user interface. UI threads work based on a simple queue system, where tasks are stored until the process is idle. Once idle, the next task in the queue is extracted and run again. These tasks are either running JavaScript code or performing UI updates, including redrawing and reordering (discussed in Chapter 3).

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, </button id=' BTN 'onclick="handleClick"></button> <script> function handleClick() { var div = document.createElement('div') div.innerHTML = 'Clicked' document.body.appendChild(div) } </script> </body> </html>Copy the code

When a button is clicked, two tasks are triggered, a button style change and a click event execution, and these two tasks are put into the UI Queue. If new tasks are encountered, Queue them into the UI Queue, and continue to execute them when the UI is idle, as shown in a graph:

When all the UI thread tasks have been executed, the process enters the idle state and waits for more tasks to be queued. The idle state is ideal because all user interactions trigger UI updates immediately. If the user tries to interact with the page while the task is running, not only are there no immediate UI updates, but new UI update tasks may not even be created and queued.

The browser is very smart, the running time of the JavaScript task is limited, this type of limit is divided into two categories: stack size limit (discussed in chapter 4) and long running script limit.

  • Chrome doesn’t have a separate limitation on long running scripts, and instead relies on its general crash detection system to handle such issues.
  • Firefox’s default limit is 10 seconds; This restriction is recorded in the browser configuration Settings (accessed by typing about:config in the address bar) with the key name dom.max_script_run_time. Support to modify

  • Opera has no limitations on long running scripts
  • Safari’s default limit is 5 seconds; This limit cannot be changed, but you can Disable the Timer by selecting Disable Runaway JavaScript Timer from the Develop menu.

Just because the browser gives you a time limit doesn’t mean you can let the code run that long, even 1s turns out to be too long for the script to run. A single JavaScript cannot run for more than 100ms.

Segmentation tasks

How do you optimize JavaScript code when it takes a long time to execute? Since JavaScript execution gets in the way of UI rendering, wouldn’t we be able to solve the problem by simply letting long-running code give time to UI rendering in the process of execution?

The following code tells the browser to wait for 2s and insert a task to the UI queue that executes the greeting function

    function greeting() {
      alert(2)}setTimeout(greeting, 2000)
Copy the code

We can use delayers to handle long arrays:

const arr = [];
 
for(let i = 0; i < 10000; i ++) {
  arr.push(i)
}
 
function handleArr(item) {
  // ...
  console.log(item)
}
 
 
function processArray(arr, process,callback) {
  const _arr = arr.concat();
  function test() {
    process(_arr.shift())
    if(_arr.length > 0) {
      setTimeout(test, 25)}else {
      callback()
    }
  
  }
  setTimeout(test, 25)
}
 
processArray(arr,handleArr, () = > {
  console.log('processArray')})Copy the code

You can also use this method to split up different tasks:

function openDocument() {
   // ...
}
function addText() {
  // ...
}
function closeDocument() {
   // ...
}
 
function saveDocument(id) {
  const arr = [openDocument, addText, closeDocument]
  function test() {
    const process =  arr.shift();
    typeof process === 'function' &&  process.call(this, id)
    if(arr.length) {
      setTimeout(test, 25)}}setTimeout(test, 25)}Copy the code

Encapsulated as a generic function:

function multistep(arr, args, callback) {
  if(!Array.isArray(arr)) {
    console.error('The first argument is an array');
    return
  }
  const _arr = arr.concat();
  function excu() {
    const process = _arr.shift();
    typeof process === 'function' && process.call(null, args)
    if(_arr.length) {
      setTimeout(excu, 25)}else {
      typeof callback === 'function' && callback()
    }
  }
  setTimeout(excu, 25)}Copy the code

If a function executes only one of the array functions at a time, it may not take long to execute. If it does, you can allow multiple array tasks to be executed more efficiently:

const arr = [openDocument, addText, closeDocument]
 
function multistep(arr, args, callback) {
  if(!Array.isArray(arr)) {
    console.error('The first argument is an array');
    return
  }
  const _arr = arr.concat();
  let  timer;
  function excu() {
    const preDate = new Date(a);do {
      const process = _arr.shift();
      typeof process === 'function' && process.call(null, args);
      console.log(3)}while( _arr.length && new Date() - preDate < 50);
    if(_arr.length) {
      timer = setTimeout(excu, 25)}else {
      typeof callback === 'function' && callback()
    }
  }
 timer = setTimeout(excu, 25)
}
multistep(arr, 8.() = > {
  console.log('h')})Copy the code

In Firefox 3, if process () is an empty function, processing an array of 1 000 items takes 38 to 43 milliseconds; The original function takes more than 25, 000 milliseconds to process the same array. This is where timed tasks come in, to avoid breaking tasks into too many fragments. The timer sequence used in the code prevents the creation of too many timers from affecting performance.

Worker

Consider the example of parsing a large JSON string (JSON parsing will be discussed in Chapter 7). Assuming a large enough amount of data, it takes at least 500 milliseconds to complete the parsing. This is obviously too long for the client to allow JavaScript to run, because it interferes with the user experience. However, this task is difficult to be decomposed into several small tasks using timers, so Worker becomes the most ideal solution. The following code depicts its use in a web page:

worker.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, > <title>Document</title> </head> <body> <div>live-srver</div> <script> const worker = new Worker('core.js'); worker.onmessage = function(event) { console.log(event.data) } worker.postMessage({a: "maile"}) </script> </body> </html>Copy the code

core.js

self.onmessage = function(event) {
  jsonObj(event.data)
  self.postMessage(event.data)
}
 
function jsonObj(obj) {
  // ...
}
Copy the code

Browsers generally do not allow workers to be used locally. Live-server can be used to quickly start a local server for testing. You can also install a live server plug-in in vscode.

The Worker operating environment consists of the following parts:

● A Navigator object with only four properties: appName, appVersion, User Agent, and Platform

● A location object (same as window.location, but all properties are read-only)

● A self object pointing to the global worker object

● An importScripts () method that loads external JavaScript files used by the Worker

● All ECMAScript objects, such as Object, Array, Date, etc

● XMLHttpRequest constructor

● setTimeout () and setInterval () methods

● A close () method that immediately stops Worker operations

The Worker loads external JavaScript files through the importScripts () method

importScripts('file1.js'.'file2.js')
 
self.onmessage = function(event) {
  jsonObj(event.data)
  self.postMessage(event.data)
}
 
function jsonObj(obj) {
  // ...
}
Copy the code

Usage Scenarios:

Parsing a large string is just one of many tasks that benefit Web Workers. Other tasks that might benefit are as follows:

● Encode/decode large strings

const num1 = 3;
const num2 = 4;
var res0 = eval("num1 + num2");
console.log(res0) / / 7
 
var res = new Function('arg1'.'arg2'."return arg1 + arg2");
console.log(res(num1, num2)) / / 7
 
var res1;
setTimeout("res1 = num1 + num2".100) // setInterval
  console.log(res1) / / 7
},100)
 
Copy the code

● Large array sort

Any process over 100 milliseconds should consider whether the Worker scheme is more appropriate than the timer based scheme. Assuming, of course, that the browser supports Web Workers.

Six Programming practices

Avoid double evaluation

JavaScript, like many other scripting languages, allows you to extract a string of code from a program and then execute it dynamically. There are four standard methods: eval (), the Function () constructor, setTimeout (), and setInterval (). Each of these methods allows you to pass in a string of JavaScript code and execute it. Let’s look at a few examples:

Use Object/Array direct quantities

Technically, the second is fine, but the first is faster, as are arrays.

/ / faster
var myObject = {
  name: "maile".count: 2.age: 15.parent: 2
}
 
/ / slowly
var otherObject = new Object()
otherObject.name = "maile";
otherObject.count = '1';
otherObject.age = '15';
otherObject.parent = '2';
Copy the code

Lazy loading

At first glance these functions appear to be fully optimized. The hidden performance problem is that the work is repeated every time a function is called, because the check is the same every time: to see if the specified method exists. If you assume that the only value of target is a DOM object, and that the user cannot magically change the browser after the page loads, then this check is repetitive.

function addHandle(target, type, fun) {
  if(target.addEventListener) {
    target.addEventListener(type, fun)
  } else {
    target.attachEvent("on" + type, fun)
  }
}
Copy the code

Can be optimized as:

function addHandle(target, type, fun) {
  if(target.addEventListener) {
    addHandle = function(target, type, fun){
      target.addEventListener(type, fun)
    }
  } else {
    addHandle = function(target, type, fun) {
      target.attachEvent("on" + type, fun)
    }
  }
  addHandle(target, type, fun)
}
function removeHandle(target, type, fun) {
  if(target.removeEventListener) {
    removeHandle = function(target, type, fun){
      target.removeEventListener(type, fun)
    } 
  } else {
    removeHandle = function(target, type, fun){
      target.detachEvent(type, fun)
    } 
  }
  removeHandle(target, type, fun)
}
Copy the code

Bit operation

Bitwise AND &

If the corresponding bit of both operands is 1, return 1 in that bit.

const num1 = 25 const num2 = 4; The console. The log (num1. ToString (2)) / / 11001 secondary system console. The log (num2. ToString (2)) / / 100 binary const res1 = num1 | num2 Console. log(res1.tostring (2)) // 11101 binary console.log(res1) // 29Copy the code

Bitwise OR Bitwise OR |

If only one of the corresponding bits of the operands is 1, it returns 1 in that bit.

const num1 = 25
const num2 = 4;
 
console.log(num1.toString(2)) // 11001 secondary system
console.log(num2.toString(2)) // 100 binary
const res1 = num1 | num2
console.log(res1.toString(2)) // 11101 binary
console.log(res1)  / / 29
Copy the code

Bitwise XOR by Bitwise XOR ^

If only one of the corresponding bits of the two operands is 1, this bit is returned. If both bits are 1, this bit is discarded

const num1 = 56
const num2 = 34;

console.log(num1.toString(2)) // 111000 secondary system
console.log(num2.toString(2)) // 100010 binary
const res1 = num1 ^ num2
console.log(res1.toString(2)) // 11010 binary
console.log(res1)  / / 26
Copy the code
const num1 = 43 const num2 = 45; Console. log(num1.tostring (2)) // 101011 console.log(num2.tostring (2)) // 101101 binary const res1 = num1 ^ num2 Console. log(res1.tostring (2)) // 110 binary console.log(res1) // 6Copy the code
const num1 = 43
const num2 = 45;

console.log(num1.toString(2)) // 101011 secondary system
console.log(num2.toString(2)) // 101101 binary
const res1 = num1 ^ num2
console.log(res1.toString(2)) // 110 binary
console.log(res1)  / / 6
Copy the code

Bitwise NOT Indicates the inverse ~ by bit

Zero returns 1 and vice versa.

const num1 = 43
 
console.log(num1.toString(2)) // 101011 secondary system
const res1 = ~num1
console.log(res1.toString(2)) // -101100 binary
console.log(res1)  / / - 44
Copy the code

The least significant of an even number is 0, and the least significant of an odd number is 1. Can be used to distinguish between odd and even numbers.

const arr = [3.67.5.2.4.7.53.1.34.5]
const evenArr=[];
const oddArr = []
arr.forEach((item) = > {
  item & 1 ? oddArr.push(item) : evenArr.push(item)
})
 
console.log(oddArr, evenArr)
Copy the code

The second technique that uses bit operations is called bitmask

During development, there are times when we need to define multiple state markers. To take a classic example of a permission operation, assume that there are four permission states as follows:

public class Permission {
	// Whether queries are allowed
	private boolean allowSelect;

	// Whether to allow the addition
	private boolean allowInsert;

	// Whether deletion is allowed
	private boolean allowDelete;

	// Whether updates are allowed
	private boolean allowUpdate;
}
Copy the code

Our goal is to determine whether or not the current user has certain permissions, and if we can say it individually, we can say four. But if you mix it up here, it’s 2 to the fourth power, 16 of them, and it’s a little bit messy. What if I had more access? And when you combine that, it goes up exponentially.

In binary, each 0 or 1 in the binary result represents the permissions of the current bit. There are 16 combinations of the four permissions, all of which are obtained through bit operation. Each factor involved in bit operation can be called a MASK.

 
const ALLOW_SELECT = 1 << 0.// 1 0001 Indicates whether query is allowed. 0 indicates no, and 1 indicates yes
      ALLOW_INSERT = 1 << 1.// 2 0010 Indicates whether to add data. 0 indicates no, and 1 indicates yes
      ALLOW_UPDATE  = 1 << 2.// 4 0100 Indicates whether to allow modification. 0 indicates no, and 1 indicates yes
      ALLOW_DELETE  = 1 << 3.// 8 1000 Indicates whether to allow modification. 0 indicates no, and 1 indicates yes
      ALLOW_UPDATE_DELETE_MASK = 12.// 1100 allows modification and deletion
      AllOW_ALL_MASK = 15 // Allow all
class Premission {
  constructor() {
    this.flag = null;
  }
   setPremission(premission) {
     this.flag = premission
   }
 
   // Enable certain permissions
 
   enable(premission) {
    this.flag = this.flag | premission
   }
 
   // Delete some permissions
 
   disable(premission) {
    this.flag = this.flag & (~premission)
   }
 
   // Whether you have some permissions
   isAllow(premission) {
     return (this.flag & premission) == premission
   }
 
   // Whether you only have certain permissions
   isOnlyAllow(premission) {
     return this.flag == premission
   }
  // Whether to disable certain permissions
   isNotAllow(premission) {
      return (this.flag & premission) == 0}}new Premission()
Copy the code

Bitmasks like this are very fast because, as mentioned earlier, the computation takes place at the bottom of the system. If many options are kept together and checked frequently, bitmasks can help improve overall performance.

Using these methods is faster than using JavaScript code with the same functionality. When you need to run complex Math operations, you should first look at the Math object.