This article is permalink: github.com/HaoChuan942…

preface

Front-end development often involves operations related to arrays: deduplication, filtering, summation, data secondary processing, and so on. Both require us to loop through the array. In order to meet various needs, JS in addition to providing the simplest for loop, in ES6 and later versions also added such as: map, filter, some, reduce and other practical methods. It is unfair and pointless to simply compare the execution speed of all the methods involved in the loop because each method works differently. So we for the most pure value for the purpose of the loop for one-time energy and efficiency test, with the naked eye visible way, the JS common in these array loop for a discussion.

Let’s start with the simplest for loop

forLoop common four ways to write, not wordy, directly on the code

const persons = ['Zheng Hao Chuan'.'Zhong Zhong'.Gao Xiaobo.'Wei Guitie'.'davids'.'Song Can']
/ / method
for (let i = 0; i < persons.length; i++) {
  console.log(persons[i])
}
/ / method 2
for (let i = 0, len = persons.length; i < len; i++) {
  console.log(persons[i])
}
/ / method 3
for (let i = 0, person; person = persons[i]; i++) {
  console.log(person)
}
4 / / method
for (let i = persons.length; i--;) {
  console.log(persons[i])
}
Copy the code
  1. The first way is the most common way, without explanation.

  2. The second method is to cache persons. Length in len so that each loop does not read the length of the array.

  3. The third way is to merge the value with the judgment and loop through each item by continually enumerating it until it reaches a null value. The order of execution is:

    • Step 1: Declare the indexi = 0And variablesperson
    • Step 2: Fetch the end of the arrayiitempersons[i]Assign values to variablespersonAnd determine whether it isTruthy
    • Step 3: Execute the body of the loop and printperson
    • Step 4:i++.

      The loop ends when the value of Person in the second step is no longer Truthy. Method three could even be written like this

      for (let i = 0, person; person = persons[i++];) {
        console.log(person)
      }
      Copy the code
  4. The fourth method is reverse cycling. The order of execution is:

    • Step 1: Get the length of the array and assign it to the variablei
    • Step 2: JudgeiWhether greater than 0 and executei--
    • Step 3: Execute the body of the loop and printpersons[i]For the time of,ialready- 1the

      I’m going to go back and forth until I equals equals zero. This approach not only eliminates the need to read the length of the array each time through the loop, but also creates only one variable, I.

Four kinds offorPerformance and speed testing in the shallow copy of arrays in the loop mode

Create an array long enough to be copied (if I is too large, it may throw a JS stack trace error)

const ARR_SIZE = 6666666
const hugeArr = new Array(ARR_SIZE).fill(1)
Copy the code

Then each item in the array is taken out and added to an empty array in four separate loops, i.e., a shallow copy of the array. The overall execution time of each loop is recorded through console.time and console.timeEnd. Process.memoryusage () compares the difference between the heap used in memory before and after execution.

/* In the node environment, record the difference between the heap used in memory before and after the method execution */
function heapRecord(fun) {
  if (process) {
    const startHeap = process.memoryUsage().heapUsed
    fun()
    const endHeap = process.memoryUsage().heapUsed
    const heapDiff = endHeap - startHeap
    console.log('Difference of used heap:', heapDiff)
  } else {
    fun()
  }
}
Copy the code
// The first method is an ordinary for loop
function method1() {
  var arrCopy = []
  console.time('method1')
  for (let i = 0; i < hugeArr.length; i++) {
    arrCopy.push(hugeArr[i])
  }
  console.timeEnd('method1')}// Method 2, cache length
function method2() {
  var arrCopy = []
  console.time('method2')
  for (let i = 0, len = hugeArr.length; i < len; i++) {
    arrCopy.push(hugeArr[i])
  }
  console.timeEnd('method2')}// Method 3: merge values and judgments
function method3() {
  var arrCopy = []
  console.time('method3')
  for (let i = 0, item; item = hugeArr[i]; i++) {
    arrCopy.push(item)
  }
  console.timeEnd('method3')}// Method 4, I -- merge with judgment and iterate in reverse order
function method4() {
  var arrCopy = []
  console.time('method4')
  for (let i = hugeArr.length; i--;) {
    arrCopy.push(hugeArr[i])
  }
  console.timeEnd('method4')}Copy the code

The above methods are called separately, each method is repeated 12 times, a maximum value and a minimum value are removed, and the average value is calculated (rounded). The final result of the execution time of each method is shown in the following table (test machine: MacBook Pro (15-inch, 2017) processor: 2.8ghz Intel Core i7 Memory: 16 GB 2133 MHz

Methods a Method 2 Methods three Methods four
For the first time, 152.201 ms 156.990 ms 152.668 ms 152.684 ms
The second time 150.047 ms 159.166 ms 159.333 ms 152.455 ms
The third time 155.390 ms 151.823 ms 159.365 ms 149.809 ms
For the fourth time 153.195 ms 155.994 ms 155.325 ms 150.562 ms
The fifth time 151.823 ms 154.689 ms 156.483 ms 148.067 ms
The sixth time 152.715 ms 154.677 ms 153.135 ms 150.787 ms
The seventh time 152.084 ms 152.587 ms 157.458 ms 152.572 ms
The eighth 152.509 ms 153.781 ms 153.277 ms 152.263 ms
The ninth 154.363 ms 156.497 ms 151.002 ms 154.310 ms
The tenth 153.784 ms 155.612 ms 161.767 ms 153.487 ms
The average time 152.811 ms 155.182 ms 155.981 ms 151.700 ms
Use the stack 238511136Byte 238511352Byte 238512048Byte 238511312Byte

Was it a surprise? Surprised? Imagine that at least method two is faster than method one! But that wasn’t the case. Not believing what I was seeing, I tested it many times, including changing the length of the array to be copied from hundreds to tens of millions. Finally, it is found that the four methods have very little difference in time to complete a shallow copy of the same array under Node, and sometimes the sort even fluctuates slightly. Memory footprint: Method 1 < method 4 < method 2 < method 3, but the gap is also small.

The new VERSION of THE V8 engine optimizes the performance of operations such as object values, so the second method to cache the array length into len will not be significantly improved. Even with millions of data, the difference in time between the four for loops is only milliseconds, and the memory footprint of the four for loops is very close. Thank you YaHuiLiang, Mr. 7sec, Gexun doxP, super pillar for your help and correction. If you have better ideas, please leave a comment.

Google chrome, also a V8 engine, found that the four methods are also very close.

However, the test results in Firefox browser: method 2 ≈ Method 3 ≈ Method 4 < Method 1, indicating that these three writing methods can optimize the for loop to some extent

In Safari browser: method four < method one ≈ method two ≈ method three, only method four body showed a small amplitude of optimization effect.

summary

For performance and efficiency in different environments or browsers:

Recommended: the fourth I – reverse cycle. This is also mentioned a little bit in the 2.3 Flow Control section of the weirdance article – Hey, here’s a Web performance optimization map.

Not recommended: Third way. This is mainly because non-Truthy values in the array, such as 0 and “, cause the loop to end directly.

whileCirculation andES6+ new syntax forEach,mapandfor ofWill it be faster?

Without further ado, practice is the sole criterion of truth

// Method 5, while
function method5() {
  var arrCopy = []
  console.time('method5')
  let i = 0
  while (i < hugeArr.length) {
    arrCopy.push(hugeArr[i++])
  }
  console.timeEnd('method5')}// Method 6,forEach
function method6() {
  var arrCopy = []
  console.time('method6')
  hugeArr.forEach((item) = > {
    arrCopy.push(item)
  })
  console.timeEnd('method6')}// Method 7,map
function method7() {
  var arrCopy = []
  console.time('method7')
  arrCopy = hugeArr.map(item= > item)
  console.timeEnd('method7')}// Method 8
function method8() {
  var arrCopy = []
  console.time('method8')
  for (let item of hugeArr) {
    arrCopy.push(item)
  }
  console.timeEnd('method8')}Copy the code

Test method is the same as above, test results:

Methods five Methods six Methods seven Methods eight
For the first time, 151.380 ms 221.332 ms 875.402 ms 240.411 ms
The second time 152.031 ms 223.436 ms 877.112 ms 237.208 ms
The third time 150.442 ms 221.853 ms 876.829 ms 253.744 ms
For the fourth time 151.319 ms 222.672 ms 875.270 ms 243.165 ms
The fifth time 150.142 ms 222.953 ms 877.940 ms 237.825 ms
The sixth time 155.226 ms 225.441 ms 879.223 ms 240.648 ms
The seventh time 151.254 ms 219.965 ms 883.324 ms 238.197 ms
The eighth 151.632 ms 218.274 ms 878.331 ms 240.940 ms
The ninth 151.412 ms 223.189 ms 873.318 ms 256.644 ms
The tenth 155.563 ms 220.595 ms 881.203 ms 234.534 ms
The average time 152.040 ms 221.971 ms 877.795 ms 242.332 ms
Use the stack 238511400Byte 238511352Byte 53887824Byte 191345296Byte

ForEach, map, and for of ES6+ syntax is not as fast as traditional for or while loops, especially the map method. However, since the map has a return value, there is no need to call the push method on the new array, so the memory footprint is low for shallow copy tasks. The for of syntax also has a memory footprint advantage. By the way, the for loop while the for loop can be broken by using the break keyword, but the forEach map loop cannot be broken.

However, with the difference of execution environment and browser, the execution speed of these syntax will also be biased or even reversed. See the figure directly:

Google Chrome

Firefox

In Safari

It can be seen that:

  1. Google ChromeES6+Looping syntax is generally slower than traditional looping syntax, but the opposite is true in Firefox and Safari.
  2. Chrome’s various loop syntax doesn’t vary much in execution time. butmapIn particular, it’s significantly slower than the other syntax, but it’s reversed in Firefox and Safari,mapIt’s faster!
  3. Apple is good

conclusion

You’ve heard things like “Cache array lengths to improve loop efficiency” or “ES6’s loop syntax is more efficient” before. The speaker is not intentional, the listener is intentional, the truth is what, the practice of true knowledge. It doesn’t make sense to talk about performance and efficiency without business scenarios and ease of use. ES6 adds a lot of array methods to facilitate front-end development, making previously complex or lengthy code, can become easy to read and refine, and good for loop writing, in the case of large amounts of data, indeed has better compatibility and multi-environment performance. Of course, the discussion in this paper is only a summary based on observation, and does not go into the bottom layer. As browsers update, the merits of these approaches may also become metaphysical. Currently, it is found that for of under Chrome Canary 70.0.3513.0 is significantly faster than Chrome 68.0.3440.84. If you have a deeper insight or post, please share it in the comments section, and this post will be a good introduction. If you are interested in the performance and efficiency of the other loop methods for arrays, try them out for yourself and feel free to comment.

Test environment: Node V10.8.0, Chrome 68.0.3440.84, Safari 11.1.2 (13605.3.8), Firefox 60.0