Home  >  Article  >  Web Front-end  >  Should you choose for loop traversal for performance?

Should you choose for loop traversal for performance?

hzc
hzcforward
2020-06-16 10:05:411988browse

The 5 forces arriving on the battlefield

Self-introduction link


for

I am the Traverse World The first prince to appear, everyone here needs to call me grandpa. I can meet most of the developer's needs.

are for , foreach , map, for...in , for.. .of

// 遍历数组
let arr = [1,2,3];
for(let i = 0;i < arr.length;i++){
    console.log(i)          // 索引,数组下标
    console.log(arr[i])     // 数组下标所对应的元素
}

// 遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
for(let i = 0, keys=Object.keys(profile); i < keys.length;i++){
    console.log(keys[i])            // 对象的键值
    console.log(profile[keys[i]])   // 对象的键对应的值
}

// 遍历字符串
let str = "abcdef";
for(let i = 0;i < str.length ;i++){
    console.log(i)          // 索引 字符串的下标
    console.log(str[i])     // 字符串下标所对应的元素
}

// 遍历DOM 节点
let articleParagraphs = document.querySelectorAll(&#39;.article > p&#39;);
for(let i = 0;i<articleParagraphs.length;i++){
    articleParagraphs[i].classList.add("paragraph");
    // 给class名为“article”节点下的 p 标签添加一个名为“paragraph” class属性。
}

forEach

I released the ES5 version. The callback function is executed once for each item in the array that contains a valid value in ascending order. Those items that have been deleted or uninitialized will be skipped (such as on sparse arrays). I am an enhanced version of the for loop.

// 遍历数组
let arr = [1,2,3];
arr.forEach(i => console.log(i))

// logs 1
// logs 2
// logs 3
// 直接输出了数组的元素

//遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
let keys = Object.keys(profile);
keys.forEach(i => {
    console.log(i)              // 对象的键值
    console.log(profile[i])     // 对象的键对应的值
})

map

I also released the ES5 version. I can create a new array. The result of the new array is the return value of each element in the original array after calling the provided function once. .

let arr = [1,2,3,4,5];
let res = arr.map(i => i * i);

console.log(res) // logs [1, 4, 9, 16, 25]

for...in enumeration

I released the ES5 version. Iterates over an object's enumerable properties except Symbol in any order.

// 遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
for(let i in profile){
    let item = profile[i];
    console.log(item)   // 对象的键值
    console.log(i)      // 对象的键对应的值

// 遍历数组
let arr = [&#39;a&#39;,&#39;b&#39;,&#39;c&#39;];
for(let i in arr){
    let item = arr[i];
    console.log(item)   // 数组下标所对应的元素
    console.log(i)      // 索引,数组下标

// 遍历字符串
let str = "abcd"
for(let i in str){
    let item = str[i];
    console.log(item)   // 字符串下标所对应的元素
    console.log(i)      // 索引 字符串的下标
}

for...of iteration

I released the ES6 version. Create an iteration loop over an iterable object (including Array, Map, Set, String, TypedArray, arguments objects, and more), invoke a custom iteration hook, and execute statements for each distinct property value.

// 迭代数组数组
let arr = [&#39;a&#39;,&#39;b&#39;,&#39;c&#39;];
for(let item of arr){
    console.log(item)     
}
// logs &#39;a&#39;
// logs &#39;b&#39;
// logs &#39;c&#39;

// 迭代字符串
let str = "abc";
for (let value of str) {
    console.log(value);
}
// logs &#39;a&#39;
// logs &#39;b&#39;
// logs &#39;c&#39;

// 迭代map
let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]
for (let entry of iterable) {
    console.log(entry);
}
// logs ["a", 1]
// logs ["b", 2]
// logs ["c", 3]

//  迭代map获取键值
for (let [key, value] of iterable) {
    console.log(key)
    console.log(value);
}


// 迭代set
let iterable = new Set([1, 1, 2, 2, 3, 3,4]);
for (let value of iterable) {
    console.log(value);
}
// logs 1
// logs 2
// logs 3
// logs 4

// 迭代 DOM 节点
let articleParagraphs = document.querySelectorAll(&#39;.article > p&#39;);
for (let paragraph of articleParagraphs) {
    paragraph.classList.add("paragraph");
    // 给class名为“article”节点下的 p 标签添加一个名为“paragraph” class属性。
}

// 迭代arguments类数组对象
(function() {
  for (let argument of arguments) {
    console.log(argument);
  }
})(1, 2, 3);
// logs:
// 1
// 2
// 3


// 迭代类型数组
let typeArr = new Uint8Array([0x00, 0xff]);
for (let value of typeArr) {
  console.log(value);
}
// logs:
// 0
// 255

After the first round of self-introduction and skill demonstration, we learned that:

  • The for statement is the most primitive loop statement. Define a variable i (numeric type, representing the subscript of the array), and perform cyclic accumulation of i according to certain conditions. The condition is usually the length of the loop object. When the length is exceeded, the loop stops. Because the object cannot determine the length, it is used with Object.keys().

  • forEach ES5 Presented. Claiming to be an enhanced version of the for statement, you can find that it is much simpler to write than the for statement. But it is essentially a loop of arrays. forEach executes the callback function once for each array element. That is the array in which it is called, so the original array will not be changed. The return value is undefined.

  • map ES5 proposed. Call the callback function once for each element in the original array in sequence. Generates a new array without modifying the original array from which it was called. The return value is the new array.

  • for...in ES5 proposed. Traverse enumerable properties on the object, including properties on the prototype object, and traverse in any order, that is, the order is not fixed. When traversing the array, the subscript of the array is used as the key value. At this time, i is a string type. It is built for iterating over object properties and is not recommended for use with arrays.

  • for...of ES6 proposed. Only iterate over the data of the iterable object.

Ability Screening


As a programmer, it is not enough to just know them. Identify them in actual development. advantages and disadvantages. Use them according to local conditions and maximize their strengths and avoid their weaknesses. Thereby improving the overall performance of the program is where the ability lies.

About jumping out of the loop body

Jump out of the loop body when certain conditions are met in the loop, or skip data that does not meet the conditions and continue looping other data. It is a requirement that is often encountered. Commonly used statements are break and continue.

Let’s briefly talk about the difference between the two, just as a review.

  • The break statement is to jump out of the current loop and execute the statement after the current loop;

  • The continue statement is to terminate the current loop and continue execution. One loop;

Note: forEach and map do not support jumping out of the loop body, but the other three methods support it.

Principle: If you look at the implementation principle of forEach, you will understand this problem.

Array.prototype.forEach(callbackfn [,thisArg]{
    
}

The function passed in is the callback function here. It is definitely illegal to use break in a callback function, because break can only be used to break out of a loop, and the callback function is not the loop body.

Using return in the callback function only returns the result to the upper-level function, that is, in this for loop, and does not end the for loop, so return is also invalid.

map() Same thing.

map() chain call

The map() method can be chained, which means it can be conveniently used in conjunction with other methods. For example: reduce(), sort(), filter(), etc. But other methods cannot do this. The return value of forEach() is undefined, so it cannot be called in a chain.

// 将元素乘以本身,再进行求和。
let arr = [1, 2, 3, 4, 5];
let res1 = arr.map(item => item * item).reduce((total, value) => total + value);

console.log(res1) // logs 55 undefined"

for...in will traverse the properties on the prototype object

Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;];
arr.foo = &#39;hello
for (var i in arr) {
    console.log(i);
}
// logs
// 0
// 1
// 2
// foo
// arrCustom
// objCustom

However, in actual development, we do not need the properties on the prototype object. In this case we can use the hasOwnProperty() method, which returns a Boolean value indicating whether the object has the specified property in its own properties (that is, whether there is the specified key). as follows:

Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;];
arr.foo = &#39;hello
for (var i in arr) {
    if (arr.hasOwnProperty(i)) {
        console.log(i);
    }
}
// logs
// 0
// 1
// 2
// foo

// 可见数组本身的属性还是无法摆脱。此时建议使用 forEach

对于纯对象的遍历,选择for..in枚举更方便;对于数组遍历,如果不需要知道索引for..of迭代更合适,因为还可以中断;如果需要知道索引,则forEach()更合适;对于其他字符串,类数组,类型数组的迭代,for..of更占上风更胜一筹。但是注意低版本浏览器的是配性。

性能

有兴趣的读者可以找一组数据自行测试,文章就直接给出结果了,并做相应的解释。

for > for-of > forEach  > map > for-in
  • for 循环当然是最简单的,因为它没有任何额外的函数调用栈和上下文;

  • for...of只要具有Iterator接口的数据结构,都可以使用它迭代成员。它直接读取的是键值。

  • forEach,因为它其实比我们想象得要复杂一些,它实际上是array.forEach(function(currentValue, index, arr), thisValue)它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,这里可能拖慢性能;

  • map() 最慢,因为它的返回值是一个等长的全新的数组,数组创建和赋值产生的性能开销很大。

  • for...in需要穷举对象的所有属性,包括自定义的添加的属性也能遍历到。且for...in的key是String类型,有转换过程,开销比较大。

总结

在实际开发中我们要结合语义话、可读性和程序性能,去选择究竟使用哪种方案。

如果你需要将数组按照某种规则映射为另一个数组,就应该用 map。

如果你需要进行简单的遍历,用 forEach 或者 for of。

如果你需要对迭代器进行遍历,用 for of。

如果你需要过滤出符合条件的项,用 filterr。

如果你需要先按照规则映射为新数组,再根据条件过滤,那就用一个 map 加一个 filter。

总之,因地制宜,因时而变。千万不要因为过分追求性能,而忽略了语义和可读性。在实际开发中,让他们扬长避短,优势互补,让程序趋近最优才是我们要做的。

推荐教程:《JS教程

The above is the detailed content of Should you choose for loop traversal for performance?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete