Hello everyone, I’m Lin Yiyi. This is an article comparing the principles and performance of the three types of loops in JS. I hope I can bring you some help 😁
Performance comparison
Performance comparison of for loop and while loop
let arr = new Array(999999).fill(1)
console.time('forTime')
for(let i = 0; i< arr.length; i++){}
console.timeEnd('forTime')
console.time('whileTime')
let i = 0
while(i< arr.length){
i ++
}
console.timeEnd('whileTime')
/* 输出
* forTime: 4.864990234375 ms
* whileTime: 8.35107421875 ms
*/
- Using
let
statement, due to thefor
of the block-level scope in 060d19190829ca, the memory is released, and the running speed will be faster. - When using the
var
statement, becausefor while
does not have the influence of block-level scope, the speed of the two is basically the same.
forEach(callback, thisArg) loop array
callback
function will be executed once in each cycle, and it can also receive three parameters(currentValue, index, array)
,index, array
is also optional, andthisArg
(optional) is thethis
point of the callback function.
Iterate over enumerable properties
let arr = new Array(999999).fill(1) console.time('forEachTime') arr.forEach(item =>{} ) console.timeEnd('forEachTime') // forEachTime: 25.3291015625 ms
- Functional programming of
forEach
consumes more performance.
Thinking: Can using return in forEach interrupt the loop?
[1,2,4,5].forEach((item, index) => {
console.log(item, index)
return
})
// 1 0
// 2 1
// 4 2
// 5 3
It can be seen from the above that the use of return in forEach cannot break out of the loop.
So how to interrupt the forEach loop ,
- You can use try catch
- Or use other loops instead, such as replacing forEach with every and some. If the internal returns false in every, it jumps out, and it jumps out when the inside is true in some.
Simulate forEach
Array.prototype.myForEach = function (callback, context) {
let i = 0,
than = this,
len = this.length;
context = context ? window : context;
for (; i < len; i++) {
typeof callback === 'function' ? callback.call(context, than[i], i, than) : null
}
}
let arr = [0, 1, 5, 9]
arr.myForEach((item, index, arr) => {
console.log(item, index, arr)
})
//0 0 (4) [0, 1, 5, 9]
// 1 1 (4) [0, 1, 5, 9]
The results are accurate. Regarding the use of this point or call, you can see JS this points to and The analog implementation of call, apply, bind
for in loop
The cycle performance offor in
The reason for the poor performance is because:for in
will iterate all the attributes that can be enumeratedon the object prototype chain.
let arr = new Array(999999).fill(1)
console.time('forInTime')
for(let key in arr){}
console.timeEnd('forInTime')
// forInTime: 323.08984375 ms
for in
loop is mainly used for objectslet obj = { name: '林一一', age: 18, 0: 'number0', 1: 'number1', [Symbol('a')]: 10 } Object.prototype.fn = function(){} for(let key in obj){ // if(!obj.hasOwnProperty(key)) break 阻止获取原型链上的公有属性 fn console.log(key) } /* 输出 0 1 name age fn */
- (Disadvantages)
for in
loop mainly traverses numbers first, traversing from small to large - (Disadvantages)
for in
cannot traverseSymbol
attributes (not enumerable). (Disadvantages)
for in
will also traverse the enumerable properties in the public (prototype). You can usehasOwnProperty
to prevent traversal of public attributes.Thinking
1. How to get Symbol attribute
Use
Object.getOwnPropertySymbols()
to get all Symbol attributes.let obj = { name: '林一一', age: 18, 0: 'number0', 1: 'number1', [Symbol('a')]: 10 } Object.prototype.fn = function(){} let arr = Object.keys(obj).concat(Object.getOwnPropertySymbols(obj)) console.log(arr) //["0", "1", "name", "age", Symbol(a)]
for of loop
let arr = new Array(999999).fill(1)
console.time('forOfTime')
for(const value of arr){}
console.timeEnd('forOfTime')
// forOfTime: 33.513916015625 ms
The principle of the for of loop is that according to whether there is an iterator specification. All those with Symbol.iterator
implement the iterator specification. For example, some of the arrays, Set,Map...
and objects do not implement the Symbol.iterator specification, so they cannot be used. for of
cycle.
- Use the
for of
loop, first execute the function corresponding to theSymbol.iterator
- The object contains a function
next()
which is executed once in anext()
next()
, and another object is returned in 060d19190831fe This object contains two values
done: represents whether the loop is over, true represents the end; value: represents the value returned each time.
// Symbol.iterator 内部机制如下 let arr = [12, 23, 34] arr[Symbol.iterator] = function () { let self = this, index = 0; return { next() { if(index > self.length-1){ return { done: true, value: undefined } } return { done: false, value: self[index++] } } } }
Thinking, how to make ordinary array-like can use for of loop
Array-like is required to have and the result attribute name of the array-like test
0, 1, 2...
, and must have thelength
attributelet obj = { 0: 12, 1: '林一一', 2: 'age18', length: 3 } // obj[Symbol.iterator] = Array.prototype[Symbol.iterator] for (const value of obj) { console.log(value) }
- 12
- Lin Yiyi
age18
*/> 只需要给类数组对象添加`Symbol.iterator`接口规范就可以了。
(Additional) Turn the argument set into a real array
arguments
not an array?
arguments
is an array of class (in fact, an object) discharging property is zero, 1, 2, ... Finally, there werethe callee and length properties,
arguments
the__proto__
directly to the base classobject
, does not have an array of methods .Method one uses call(), [].slice/Array.prototype.slice()
let array = [12, 23, 45, 65, 32] function fn(array){ var args = [].slice.call(arguments) return args[0] } fn(array) // [12, 23, 45, 65, 32]
above
slice
combinationcall
Why can changethis
can postarguments
converted into an array? Let’s simulate handwriting and implementslice
, and we will know the principleArray.prototype.mySlice = function(startIndex=0, endIndex){ let array = this // 通过 this 获取调用的数组 let thisArray = [] endIndex === undefined ? (endIndex = array.length) : null for(let i = startIndex; i< endIndex; i++){ // 通过 `length` 属性遍历 thisArray.push(array[i]) } return thisArray } // 测试一下没有问题 let arr = [1, 3, 5, 6, 7, 23] let a a = arr.mySlice() // [1, 3, 5, 6, 7, 23] a = arr.mySlice(2, 6) // [5, 6, 7, 23]
By
this
gets calledmySlice
array, and then bylength
traversing the formation of a new array property returns. So changethis
to point toarguments
and thenarguments.length
to return a new array to convert the class array into an array.
Come to think about the string can be converted into an array?
let a = [].slice.call('stringToArray')
console.log(a) // ["s", "t", "r", "i", "n", "g", "T", "o", "A", "r", "r", "a", "y"]
The same is also possible, for the same reason as above. As for why the string (value type) is this
, you can take a look at this article [Interview | call, apply, bind implementation principles and interview questions] ()
Method 2: Use ES6's spread operator ...
function fn(array){
var args = [...arguments]
return args
}
fn(12, 23, 45, 65, 32) // [12, 23, 45, 65, 32]
Way three Array.from()
function fn(array){
return Array.from(arguments)
}
fn(12, 23, 45, 65, 32) // [12, 23, 45, 65, 32]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。