>  기사  >  웹 프론트엔드  >  성능을 위해 루프 순회를 선택해야 합니까?

성능을 위해 루프 순회를 선택해야 합니까?

hzc
hzc앞으로
2020-06-16 10:05:412052검색

전장에 도착한 5군

자기소개 시간


for

나는 횡단세계에 처음 등장한 왕자야, 여기 있는 모두는 나를 할아버지라고 불러야 해. 나는 개발자의 요구를 대부분 충족시킬 수 있습니다.

각각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

ES5 버전을 출시했습니다. 콜백 함수는 유효한 값이 오름차순으로 포함된 배열의 각 항목에 대해 한 번씩 실행됩니다. 삭제되거나 초기화되지 않은 항목은 건너뜁니다(예: 희소 배열). 저는 for 루프의 향상된 버전입니다.

// 遍历数组
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

ES5 버전에도 출시되었습니다. 새 배열을 만들 수 있습니다. 새 배열의 결과는 원래 배열의 각 요소에 대해 제공된 함수를 한 번 호출한 후 반환 값입니다.

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

ES5 버전을 출시했습니다. Symbol을 제외한 객체의 열거 가능한 속성을 어떤 순서로든 반복합니다.

// 遍历对象
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

ES6 버전을 출시했습니다. 반복 가능한 객체(Array, Map, Set, String, TypedArray, 인수 객체 등 포함)에 대한 반복 루프를 만들고, 사용자 정의 반복 후크를 호출하고, 각 고유 속성 값에 대한 명령문을 실행합니다.

// 迭代数组数组
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

1차 자기소개 및 기술 시연 후에 우리는 다음을 배웠습니다.

  • for 문은 가장 원시적인 루프 문입니다. 변수 i(배열의 첨자를 나타내는 숫자 유형)를 정의하고 특정 조건에 따라 i의 순환 누적을 수행합니다. 조건은 일반적으로 루프 개체의 길이입니다. 길이를 초과하면 루프가 중지됩니다. 객체는 길이를 결정할 수 없기 때문에 Object.keys()와 함께 사용됩니다.

  • forEach ES5가 제공됩니다. for 문의 향상된 버전이라고 주장하면 for 문보다 작성하기가 훨씬 간단하다는 것을 알 수 있습니다. 그러나 이는 본질적으로 배열의 루프입니다. forEach는 각 배열 요소에 대해 콜백 함수를 한 번씩 실행합니다. 이것이 호출되는 배열이므로 원래 배열은 변경되지 않습니다. 반환 값은 정의되지 않았습니다.

  • ES5에서 제안한 지도입니다. 원래 배열의 각 요소에 대해 순서대로 콜백 함수를 한 번씩 호출합니다. 호출된 원래 배열을 수정하지 않고 새 배열을 생성합니다. 반환 값은 새 배열입니다.

  • for...ES5에서 제안되었습니다. 프로토타입 객체의 속성을 포함하여 객체의 열거 가능한 속성을 순회하고 순서에 상관없이 순회합니다. 즉, 순서는 고정되지 않습니다. 배열을 순회할 때 배열의 첨자를 키 값으로 사용합니다. 이때 i는 문자열 유형입니다. 객체 속성을 반복하기 위해 만들어졌으며 배열과 함께 사용하는 것은 권장되지 않습니다.

  • for...of ES6이 제안되었습니다. 반복 가능한 객체의 데이터만 반복합니다.

능력심사


프로그래머로서 단순히 아는 것만으로는 부족하고, 실제 개발에서는 각각의 장단점을 파악해야 합니다. 현지 상황에 따라 활용하여 강점을 극대화하고 약점을 피하세요. 따라서 프로그램의 전반적인 성능을 향상시키는 것이 능력이 있는 곳입니다.

루프 본문에서 빠져나오는 것에 대하여

루프에서 특정 조건이 만족되면 루프 본문을 빠져 나오거나, 조건에 맞지 않는 데이터를 건너뛰고 다른 데이터를 계속해서 루프합니다. 자주 접하게 되는 요구 사항입니다. 일반적으로 사용되는 문은 break와 continue입니다.

복습을 위해 둘 사이의 차이점에 대해 간략하게 이야기해 보겠습니다.

  • break 문은 현재 루프에서 벗어나 현재 루프 이후에 문을 실행하는 것입니다.

  • continue 문은 현재 루프를 종료하고 다음 루프를 계속 실행하는 것입니다. 및 맵은 브레이크아웃을 지원하지 않습니다. 루프 본문의 경우 다른 세 가지 방법이 지원됩니다.

  • 원리: forEach의 구현 원리를 살펴보면 이 문제를 이해할 수 있습니다.
Array.prototype.forEach(callbackfn [,thisArg]{
    
}

여기에 전달된 함수는 콜백 함수입니다. 콜백 함수에서 break를 사용하는 것은 확실히 불법입니다. break는 루프에서 벗어나는 데에만 사용할 수 있고 콜백 함수는 루프 본문이 아니기 때문입니다.

콜백 함수에서 return을 사용하면 상위 함수, 즉 이 for 루프에만 결과를 반환하고 for 루프는 종료되지 않으므로 return도 유효하지 않습니다.

map()도 마찬가지입니다.

map() chain call

map() 메서드는 체인으로 연결될 수 있으므로 다른 메서드와 함께 편리하게 사용할 수 있습니다. 예: Reduce(), sort(), filter() 등 그러나 다른 방법으로는 이를 수행할 수 없습니다. forEach()의 반환 값은 정의되지 않았으므로 체인에서 호출할 수 없습니다.

// 将元素乘以本身,再进行求和。
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은 프로토타입 객체의 속성을 순회합니다

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

그러나 실제 개발에서는 프로토타입 객체의 속성이 필요하지 않습니다. 이 경우 객체의 자체 속성에 지정된 속성이 있는지(즉, 지정된 키가 있는지 여부)를 나타내는 부울 값을 반환하는 hasOwnProperty() 메서드를 사용할 수 있습니다. 다음과 같습니다:

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教程

위 내용은 성능을 위해 루프 순회를 선택해야 합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제