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
for
Loop 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
-
The first way is the most common way, without explanation.
-
The second method is to cache persons. Length in len so that each loop does not read the length of the array.
-
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 index
i = 0
And variablesperson
- Step 2: Fetch the end of the array
i
itempersons[i]
Assign values to variablesperson
And determine whether it isTruthy - Step 3: Execute the body of the loop and print
person
- 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
- Step 1: Declare the index
-
The fourth method is reverse cycling. The order of execution is:
- Step 1: Get the length of the array and assign it to the variable
i
- Step 2: Judge
i
Whether greater than 0 and executei--
- Step 3: Execute the body of the loop and print
persons[i]
For the time of,i
already- 1
the
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.
- Step 1: Get the length of the array and assign it to the variable
Four kinds offor
Performance 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.
while
Circulation andES6+ new syntax forEach
,map
andfor of
Will 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:
- Google Chrome
ES6+
Looping syntax is generally slower than traditional looping syntax, but the opposite is true in Firefox and Safari. - Chrome’s various loop syntax doesn’t vary much in execution time. but
map
In particular, it’s significantly slower than the other syntax, but it’s reversed in Firefox and Safari,map
It’s faster! - 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