배열 반복과 함께“for… in”을 사용하는 것이 나쁜 생각 인 이유는 무엇입니까?


1823

for...inJavaScript에서 배열과 함께 사용하지 말라고 들었습니다 . 왜 안돼?


45
나는 누군가가 당신에게 그 말을 한 최근의 질문을 보았지만 그들은 단지 배열을 의미했습니다. 배열을 반복하는 것은 좋지 않은 것으로 간주되지만 반드시 객체의 멤버를 반복하는 것은 아닙니다.
mmurch

19
'for (var i = 0; i <hColl.length; i ++) {}'와 같은 "for"루프가있는 많은 답변은 'var i = hColl.length; 반면 (i--) {} '는 후자의 형태를 사용할 수있을 때 실질적으로 더 빠릅니다. 나는 이것이 접선이라는 것을 알고 있지만이 비트를 추가 할 것이라고 생각했습니다.
Mark Schultheiss 2016 년

2
@MarkSchultheiss 그러나 그것은 역 반복입니다. 더 빠른 다른 순방향 반복 버전이 있습니까?
ma11hew28

5
@Wynand는 var i = hCol1.length; for (i;i;i--;) {}캐시 i를 사용하여 차이를 만들고 테스트를 단순화합니다. - 브라우저 오래된 사이의 더 차이 for와는 while항상 "나"카운터를 캐시 - 물론 부정적인 항상 상황과 부정적인 반면에 맞지 않는 obfuscate 사람들에 대한 코드 조금. 그리고 노트 var i = 1000; for (i; i; i--) {}var b =1000 for (b; b--;) {}내가 1 1000 간다와 b 999 0으로 어디로 - 오래된 브라우저를수록하면서는 성능을 선호하는 경향이있다.
Mark Schultheiss

9
당신은 또한 영리 할 수 ​​있습니다. for(var i = 0, l = myArray.length; i < l; ++i) ...순방향 반복으로 얻을 수있는 가장 빠르고 최상의 방법입니다.
Mathieu Amiot

답변:


1557

그 이유는 하나의 구성입니다.

var a = []; // Create a new empty array.
a[5] = 5;   // Perfectly legal JavaScript that resizes the array.

for (var i = 0; i < a.length; i++) {
    // Iterate over numeric indexes from 0 to 5, as everyone expects.
    console.log(a[i]);
}

/* Will display:
   undefined
   undefined
   undefined
   undefined
   undefined
   5
*/

때로는 다른 것과 완전히 다를 수 있습니다.

var a = [];
a[5] = 5;
for (var x in a) {
    // Shows only the explicitly set index of "5", and ignores 0-4
    console.log(x);
}

/* Will display:
   5
*/

또한 JavaScript 라이브러리는 다음과 같은 작업을 수행하여 생성하는 모든 배열에 영향을 줄 수 있습니다.

// Somewhere deep in your JavaScript library...
Array.prototype.foo = 1;

// Now you have no idea what the below code will do.
var a = [1, 2, 3, 4, 5];
for (var x in a){
    // Now foo is a part of EVERY array and 
    // will show up here as a value of 'x'.
    console.log(x);
}

/* Will display:
   0
   1
   2
   3
   4
   foo
*/


146
역사적으로 일부 브라우저는 '길이', 'toString'등을 반복했습니다!
bobince

398
글로벌을 만들지 (var x in a)말고 사용하십시오 (x in a).
Chris Morgan

78
첫 번째 문제는 그것이 나쁜 이유가 아니라 의미의 차이점입니다. 두 번째 문제는 내장 데이터 유형의 프로토 타입을 변경하는 것이 ..in이 아닌 나쁘다는 이유 (같은 작업을 수행하는 라이브러리 간 충돌)입니다.
Stewart

86
@Stewart : JS의 모든 객체 는 연관되어 있습니다. JS Array는 객체이므로 그렇습니다.하지만 연관성이 없습니다. 객체의 를 반복 하려면을 사용하십시오 for (var key in object). 그러나 배열의 요소 를 반복 하려면을 사용하십시오 for(var i = 0; i < array.length; i += 1).
Martijn

42
당신이 그 첫 번째 예를 들어 말했다 모든 사람이 예상하는 바와 같이, 0에서 4까지의 숫자 인덱스로 반복 , 나는 0에서 5까지 반복 그것을 기대 ! 위치 5에 요소를 추가하면 배열에 6 개의 요소 (5 개는 정의되지 않음)가 있습니다.
stivlo

393

for-in문장 자체는 "나쁜 습관"이 아니지만 배열이나 배열 같은 객체 를 반복 하는잘못 사용할 수 있습니다 .

for-in문장 의 목적은 객체 속성 을 열거 하는 것입니다. 이 문장은 프로토 타입 체인에서 올라가고 때로는 원하지 않는 상속 된 속성을 열거 합니다.

또한 스펙에 의해 반복 순서가 보장되지는 않습니다. 즉,이 명령문을 사용하여 배열 오브젝트를 "반복"하려는 경우 특성 (배열 색인)이 숫자 순서대로 방문되는지 확신 할 수 없습니다.

예를 들어, JScript (IE <= 8)에서 Array 객체에서도 열거 순서는 속성이 생성 될 때 정의됩니다.

var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';

for (var p in array) {
  //... p will be "2", "1" and "0" on IE
}

또한 상속 된 속성에 대해 말하면 (예를 들어 Array.prototypeMooTools와 같은 일부 라이브러리와 같이) 개체를 확장하면 해당 속성도 열거됩니다.

Array.prototype.last = function () { return this[this.length-1]; };

for (var p in []) { // an empty array
  // last will be enumerated
}

이전에 배열이나 배열과 같은 객체 를 반복 하기 위해 말했듯 이 가장 좋은 방법은 일반 낡은 / 루프 와 같은 순차적 루프 를 사용하는 것입니다 .forwhile

상속되지 않은 객체 의 고유 한 속성 만 열거 하려면 다음 hasOwnProperty방법을 사용할 수 있습니다 .

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    // prop is not inherited
  }
}

그리고 어떤 사람들은 객체에 Object.prototype이름이 지정된 속성을 추가하는 경우 문제가 발생하지 않도록 직접 메서드를 호출하는 것이 좋습니다 hasOwnProperty.

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }
}

10
David Humphrey의 게시물 JavaScript에서 객체를 빠르게 반복 -배열 for..in은 "정상"루프보다 훨씬 느립니다.
Chris Morgan

17
"hasOwnProperty"에 대한 마지막 요점에 대한 질문 : 누군가가 개체의 "hasOwnProperty"를 재정의하면 문제가 발생합니다. 그러나 누군가 "Object.prototype.hasOwnProperty"를 재정의하면 같은 문제가 발생하지 않습니까? 어느 쪽이든 그들이 당신을 망치고 당신의 책임이 아닌가?
Scott Rippey

당신 for..in은 나쁜 습관이 아니라고 말하지만 잘못 사용될 수 있습니다. 상속 된 속성을 포함한 모든 객체 속성을 실제로 살펴보고 싶었던 모범 사례의 실제 예를 얻었습니까?
rjmunro

4
@ScottRippey : 당신이 그것을 가지고 싶다면 : youtube.com/watch?v=FrFUI591WhI
Nathan Wall

이 답변으로 나는for (var p in array) { array[p]; }
equiman

117

for..in배열 요소를 반복 하는 데 사용 해서는 안되는 세 가지 이유가 있습니다 .

  • for..in배열 객체가 아닌 상속받은 모든 속성을 반복합니다 DontEnum. 즉, 누군가가 특정 배열 객체에 속성을 추가하거나 (이에 대한 정당한 이유가 있습니다.) 직접 변경했거나 Array.prototype다른 스크립트와 잘 작동하는 코드에서는 나쁜 습관으로 간주됩니다. 또한 반복된다; 상속 된 속성은을 확인하여 제외 할 수 hasOwnProperty()있지만 배열 객체 자체에 설정된 속성으로는 도움이되지 않습니다.

  • for..in 요소 순서를 유지한다고 보장되지 않습니다

  • 배열 객체의 모든 속성과 전체 프로토 타입 체인을 걸어야하므로 속성 이름 만 가져옵니다. 즉, 값을 얻으려면 추가 조회가 필요합니다.


55

for ... in은 배열 자체가 아닌 배열을 보유하는 객체를 통해 열거하기 때문입니다. 배열 프로토 타입 체인에 함수를 추가하면 포함됩니다. 즉

Array.prototype.myOwnFunction = function() { alert(this); }
a = new Array();
a[0] = 'foo';
a[1] = 'bar';
for(x in a){
 document.write(x + ' = ' + a[x]);
}

이것은 쓸 것이다 :

0 = 푸
1 = 바
myOwnFunction = function () {경고 (this); }

프로토 타입 체인에 아무것도 추가되지 않을 것이라고 확신 할 수 없으므로 for 루프를 사용하여 배열을 열거하면됩니다.

for(i=0,x=a.length;i<x;i++){
 document.write(i + ' = ' + a[i]);
}

이것은 쓸 것이다 :

0 = 푸
1 = 바

16
배열 객체이며 "배열을 보유하는 객체"는 없습니다.
RobG

41

격리 된 배열에서 for-in을 사용하는 데 아무런 문제가 없습니다. For-in은 개체의 속성 이름을 반복하며 "기본 제공"배열의 경우 속성은 배열 인덱스에 해당합니다. (등 propertes 내장 length, toString등등 반복에 포함되지 않으며.)

그러나 코드 (또는 사용중인 프레임 워크)가 사용자 지정 속성을 배열 또는 배열 프로토 타입에 추가하는 경우 이러한 속성이 반복에 포함되며 이는 아마도 원하는 것이 아닙니다.

프로토 타입과 같은 일부 JS 프레임 워크는 어레이 프로토 타입을 수정합니다. JQuery와 같은 다른 프레임 워크는 그렇지 않으므로 JQuery를 사용하면 안전하게 사용할 수 있습니다.

확실치 않다면 아마도 for-in을 사용해서는 안됩니다.

배열을 반복하는 다른 방법은 for-loop를 사용하는 것입니다.

for (var ix=0;ix<arr.length;ix++) alert(ix);

그러나 이것은 다른 문제가 있습니다. 문제는 JavaScript 배열에 "구멍"이있을 수 있다는 것입니다. 다음 arr과 같이 정의 하면 :

var arr = ["hello"];
arr[100] = "goodbye";

그런 다음 배열에는 두 개의 항목이 있지만 길이는 101입니다. for-in을 사용하면 두 개의 인덱스가 생성되고 for-loop는 101의 인덱스를 생성하며 99의 값은 undefined입니다.


37

2016 년 (ES6) 기준으로 for…ofJohn Slegers가 이미 알았 으로 배열 반복에 .

이 간단한 데모 코드를 추가하여 더 명확하게 만들고 싶습니다.

Array.prototype.foo = 1;
var arr = [];
arr[5] = "xyz";

console.log("for...of:");
var count = 0;
for (var item of arr) {
    console.log(count + ":", item);
    count++;
    }

console.log("for...in:");
count = 0;
for (var item in arr) {
    console.log(count + ":", item);
    count++;
    }

콘솔은 다음을 보여줍니다 :

for...of:

0: undefined
1: undefined
2: undefined
3: undefined
4: undefined
5: xyz

for...in:

0: 5
1: foo

다시 말해:

  • for...of0에서 5까지 계산하고을 무시합니다 Array.prototype.foo. 배열 값을 보여줍니다 .

  • for...in5정의되지 않은 배열 인덱스는 무시하고을 추가 하여 , 만 나열합니다 foo. 배열 속성 이름을 보여줍니다 .


32

짧은 대답 : 가치가 없습니다.


더 긴 대답 : 순차적 요소 순서와 최적의 성능이 필요하지 않더라도 가치가 없습니다.


긴 대답 : 가치가 없습니다 ...

  • 를 사용 for (var property in array)하면 객체array 로 반복 되어 객체 프로토 타입 체인을 통과하여 궁극적으로 인덱스 기반 루프 보다 느리게 수행 됩니다.for
  • for (... in ...) 예상대로 객체 속성을 순차적으로 반환한다고 보장 할 수는 없습니다.
  • 객체 속성을 필터링하는 데 사용 hasOwnProperty()하고 !isNaN()확인하면 추가 오버 헤드가 발생하여 성능이 더 느려지고 처음에 객체를 사용하는 주요 이유, 즉보다 간결한 형식으로 인해 무시됩니다.

이러한 이유로 성능과 편의성 사이에 적절한 균형이 존재하지 않습니다. 배열을 객체 로 처리하고 배열 의 객체 속성에 대한 작업을 수행 하지 않는 한 실제로 이점은 없습니다 .


31

루프가 객체의 속성 이름과 변수를 반복하기 때문에 카운터 변수로 수학을 수행 해야하는 경우 다른 답변에 주어진 이유 외에도 "for ... in"구조를 사용하지 않을 수 있습니다 문자열입니다.

예를 들어

for (var i=0; i<a.length; i++) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

쓸 것이다

0, number, 1
1, number, 2
...

이므로,

for (var ii in a) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

쓸 것이다

0, string, 01
1, string, 11
...

물론 이것은 포함하여 쉽게 극복 할 수 있습니다

ii = parseInt(ii);

루프에서 첫 번째 구조가 더 직접적입니다.


6
정수가 필요하거나 유효하지 않은 문자를 무시하지 않는 한 +대신 접두사 를 사용할 수 있습니다 parseInt.
Konrad Borowski

또한 사용 parseInt()하지 않는 것이 좋습니다. 시도 parseInt("025");그것은 것이다 실패합니다.
Derek 朕 會 功夫

6
@Derek 朕 會 功夫-당신은 확실히 사용할 수 있습니다 parseInt. 기수를 포함하지 않으면 오래된 브라우저가 숫자를 해석하려고 할 수 있습니다 (따라서 025가 8 진수가 됨). 이것은 ECMAScript 5에서 수정되었지만 여전히 "0x"로 시작하는 숫자 (숫자를 16 진수로 해석)에서 발생합니다. 안전한면에 있으려면 기수를 사용하여 숫자 10과 같이 숫자를 지정하십시오 parseInt("025", 10).
IAmTimCorey

23

for... in열거 가능한 모든 속성 ( "모든 배열 요소"와 동일 하지 않음)을 반복 한다는 사실 외에도 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma를 참조하십시오 . -262.pdf , 섹션 12.6.4 (5 판) 또는 13.7.5.15 (7 판) :

속성을 열거하는 메커니즘과 순서지정되지 않았습니다 ...

(엠파 시스 광산)

즉, 브라우저가 원하는 경우 삽입 된 순서대로 속성을 통과 할 수 있습니다. 또는 숫자 순서로. 또는 어휘 순서로 ( "30"이 "4"보다 앞에 오는 경우! 모든 객체 키와 모든 배열 인덱스는 실제로는 문자열이므로 전체적으로 이해해야합니다.) 객체를 해시 테이블로 구현 한 경우 버킷별로 처리 할 수 ​​있습니다. 또는 그 중 하나를 가지고 "뒤로"를 추가하십시오. 브라우저는 각 속성을 정확히 한 번 방문한 경우 무작위로 반복 하고 ECMA-262를 준수 할 수 있습니다.

실제로 대부분의 브라우저는 현재 거의 동일한 순서로 반복하고 싶어합니다. 그러나 그들이 할 말은 없습니다. 그것은 구현에 따라 다르며 다른 방법이 훨씬 더 효율적인 것으로 판명되면 언제든지 변경 될 수 있습니다.

어느 쪽이든, for... in질서의 의미가 없습니다. 순서에 관심이 있다면 명시 적으로 확인 for하고 색인이 있는 규칙적인 루프를 사용하십시오 .


18

주로 두 가지 이유 :

하나

다른 사람들이 말했듯이 배열에 없거나 프로토 타입에서 상속받은 키를 얻을 수 있습니다. 예를 들어 라이브러리가 배열 또는 객체 프로토 타입에 속성을 추가한다고 가정 해 보겠습니다.

Array.prototype.someProperty = true

모든 배열의 일부로 얻을 수 있습니다.

for(var item in [1,2,3]){
  console.log(item) // will log 1,2,3 but also "someProperty"
}

hasOwnProperty 메소드를 사용하여이를 해결할 수 있습니다.

var ary = [1,2,3];
for(var item in ary){
   if(ary.hasOwnProperty(item)){
      console.log(item) // will log only 1,2,3
   }
}

그러나 for-in 루프가있는 모든 객체를 반복하는 경우에 해당됩니다.

일반적으로 배열의 항목 순서는 중요하지만 for-in 루프는 반드시 올바른 순서로 반복 될 필요는 없습니다. 왜냐하면 배열을 객체로 취급하기 때문입니다. 배열로. 이것은 작은 것 같지만 실제로 응용 프로그램을 망칠 수 있으며 디버깅하기가 어렵습니다.


2
Object.keys(a).forEach( function(item) { console.log(item) } )프로토 타입에서 상속 된 키가 아닌 고유 한 속성 키 배열을 반복합니다.
Qwerty

2
사실이지만 for-in 루프와 마찬가지로 반드시 올바른 인덱스 순서 일 필요는 없습니다. 또한 ES5를 지원하지 않는 이전 브라우저에서는 작동하지 않습니다.
Lior

array.forEach스크립트에 특정 코드를 삽입하여 해당 브라우저 를 가르 칠 수 있습니다 . Polyfill developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Qwerty

물론 프로토 타입을 조작하는 것은 항상 좋은 생각은 아니지만 여전히 명령의 문제가 있습니다.
Lior

물론 세 번째 이유 : 스파 스 배열.
더 나은 올리버

16

인덱스가 아닌 객체 필드를 통해 열거되기 때문에. 당신은 인덱스 "길이"로 가치를 얻을 수 있고 당신이 이것을 원하지 않는 것 같습니다.


가장 좋은 방법은 무엇입니까?
lYriCAlsSH

3
for (var i = 0; i <arr.length; i ++) {}
vava

3
파이어 폭스 3에서는 arr.forEach 또는 for (var [i, v] in Iterator (arr)) {}를 사용할 수 있지만 IE에서는 이러한 기능을 사용할 수 없지만 forEach 메서드를 직접 작성할 수는 있습니다.
vava

사실상 모든 라이브러리에는 이것에 대한 고유 한 방법이 있습니다.
vava

5
이 답변은 잘못되었습니다. "길이"는 for-in 반복에 포함되지 않습니다. 자신이 추가 한 속성 만 포함됩니다.
JacquesB

16

나는 예를 들어 추가해야 할 것이 많지 않다고 생각합니다. Triptych의 답변 또는 CMS의 답변 에 대한 이유for...in 하지 않는 .

그러나 현대 브라우저 에는 for...in사용할 for...in수없는 경우에 사용할 수 있는 대안이 있다고 덧붙이고 싶습니다 . 그 대안은 for...of다음과 같습니다.

for (var item of items) {
    console.log(item);
}

노트 :

불행히도 Internet Explorer 지원 버전 for...of( Edge 12+ 는 지원 하지 않음)이 있으므로 클라이언트 측 프로덕션 코드에서 사용할 수있을 때까지 조금 더 기다려야합니다. 그러나 Node.js 를 사용하는 경우 서버 측 JS 코드에서 사용하는 것이 안전해야합니다 .


@georgeawg 당신은 for-of그렇지 for-in않습니까?
ᆼ ᆺ ᆼ

15

문제는 for ... in ...— 프로그래머가 실제로 언어를 이해하지 못하는 경우에만 문제가됩니다. 실제로 버그 나 다른 것은 아닙니다 . 객체의 모든 멤버를 반복한다는 것입니다 (물론 열거 가능한 모든 멤버이지만 세부 사항입니다). 배열의 인덱싱 된 속성 반복 하려면 의미 적으로 일관된 상태를 유지하는 유일한 방법은 정수 인덱스 ( for (var i = 0; i < array.length; ++i)스타일 루프)를 사용하는 것입니다.

모든 객체는 이와 관련된 임의의 속성을 가질 수 있습니다. 특히 배열 인스턴스에 추가 속성을로드하는 데 끔찍한 일은 없습니다. 따라서 인덱스 배열과 유사한 속성 보려는 코드 는 정수 인덱스를 사용해야합니다 . 모든 속성을 볼 필요for ... in 가 있고 모든 속성을 볼 필요 가있는 코드를 잘 알고 있으면 좋습니다.


좋은 설명 Pointy. 그냥 궁금해서 곱하기 속성 아래의 객체 내부에 배열이 for in있고 일반 for 루프와 비교하여 배열을 반복하면 해당 배열이 반복됩니까? (
실제로

2
@NiCkNewman 루프 in에서 참조한 객체 for ... in는 다음과 같습니다
Pointy

내가 참조. 메인 게임 객체 안에 객체와 배열이 있기 때문에 궁금한 점이 있으며, 이닝이 더 고통 스러울 지 인덱스에 대한 규칙적인 for 루프인지 궁금합니다.
NiCk Newman

@NiCkNewman이 전체 질문의 주제는 for ... in배열에 사용하면 안된다는 것 입니다. 하지 말아야 할 많은 이유가 있습니다. "충돌하지 않는지 확인"문제와 같은 성능 문제는 아닙니다.
Pointy

글쎄, 내 물건은 기술적으로 배열에 저장되어 있습니다. 그래서 내가 걱정 한 이유는 다음과 같습니다 [{a:'hey',b:'hi'},{a:'hey',b:'hi'}].
NiCk Newman

9

또한 시맨틱으로 인해 for, in배열을 처리 하는 방식 (즉, 다른 JavaScript 객체와 동일)은 다른 인기있는 언어와 정렬되지 않습니다.

// C#
char[] a = new char[] {'A', 'B', 'C'};
foreach (char x in a) System.Console.Write(x); //Output: "ABC"

// Java
char[] a = {'A', 'B', 'C'};
for (char x : a) System.out.print(x);          //Output: "ABC"

// PHP
$a = array('A', 'B', 'C');
foreach ($a as $x) echo $x;                    //Output: "ABC"

// JavaScript
var a = ['A', 'B', 'C'];
for (var x in a) document.write(x);            //Output: "012"

9

TL & DR :for in 배열 에서 루프를 사용하는 것은 사악한 것이 아니며 실제로는 반대입니다.

배열에서 올바르게for in 사용 하면 루프가 JS의 보석 이라고 생각합니다 . 소프트웨어를 완전히 제어하고 수행중인 작업을 알고 있어야합니다. 언급 된 단점을보고 하나씩 반증 해 봅시다.

  1. 그것은뿐만 아니라 상속 된 속성을 통해 루프 : 받는 모든 확장자의 우선 Array.prototype사용하여 수행되어 있어야 Object.defineProperty()하고 자신의 enumerable기술자로 설정해야합니다 false. 그렇게하지 않은 라이브러리는 전혀 사용하지 않아야합니다.
  2. 속성은 상속 체인에 추가 사람들은 나중에 계산 얻을 : 서브 클래스라는 배열 수행하여 때 Object.setPrototypeOf또는 클래스에 의해 extend. Object.defineProperty()기본적으로 writable, enumerableconfigurable속성 설명자를로 설정하는 것을 사용해야합니다 false. 여기에서 배열 서브 클래 싱 예제를 보자 ...

function Stack(...a){
  var stack = new Array(...a);
  Object.setPrototypeOf(stack, Stack.prototype);
  return stack;
}
Stack.prototype = Object.create(Array.prototype);                                 // now stack has full access to array methods.
Object.defineProperty(Stack.prototype,"constructor",{value:Stack});               // now Stack is a proper constructor
Object.defineProperty(Stack.prototype,"peak",{value: function(){                  // add Stack "only" methods to the Stack.prototype.
                                                       return this[this.length-1];
                                                     }
                                             });
var s = new Stack(1,2,3,4,1);
console.log(s.peak());
s[s.length] = 7;
console.log("length:",s.length);
s.push(42);
console.log(JSON.stringify(s));
console.log("length:",s.length);

for(var i in s) console.log(s[i]);

그래서 당신은 .. for in당신이 당신의 코드에 관심이 있기 때문에 루프는 이제 안전합니다.

  1. for in루프는 느린 없습니다 : 지옥 아니. 때때로 필요한 스파 스 어레이를 반복하는 경우 가장 빠른 반복 방법입니다. 이것은 알아야 할 가장 중요한 성능 트릭 중 하나입니다. 예를 보자. 희소 배열을 반복합니다.

var a = [];
a[0] = "zero";
a[10000000] = "ten million";
console.time("for loop on array a:");
for(var i=0; i < a.length; i++) a[i] && console.log(a[i]);
console.timeEnd("for loop on array a:");
console.time("for in loop on array a:");
for(var i in a) a[i] && console.log(a[i]);
console.timeEnd("for in loop on array a:");


@Ravi Shanker Reddy 좋은 벤치마킹 설정. 내 대답에서 언급했듯이 for in루프는 다른 경우 "다른 경우"보다 배열이 희박하고 더 커지면 더 커집니다. 그래서 무작위 인덱스에서 무작위 arr로 선택된 50 개의 항목으로 ~ 10000 크기 의 희소 배열에 대한 벤치 테스트를 재정렬했습니다 [42,"test",{t:1},null, void 0]. 차이점을 즉시 알 수 있습니다. ->> 여기서 확인하십시오 <<- .
Redu

8

다른 문제 외에도 인덱스가 정수가 아닌 문자열이기 때문에 "for..in"구문이 느려질 수 있습니다.

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'

아마 많은 것이 중요하지 않습니다. 배열 요소는 배열 기반 또는 배열과 유사한 객체의 속성이며 모든 객체 속성에는 문자열 키가 있습니다. JS 엔진이 어떻게 든 그것을 최적화하지 않으면 숫자를 사용하더라도 검색을 위해 문자열로 바뀝니다.
cHao

성능 문제에 관계없이 JavaScript를 처음 사용 var i in a하는 경우 색인을 정수로 사용 하고 기대하면 a[i+offset] = <value>값을 완전히 잘못된 위치에 배치합니다. ( "1"+ 1 == "11").
szmoore

8

중요한 측면은 열거 가능한 속성 속성 이 true로 설정된 for...in객체에 포함 된 속성 만 반복 한다는 것입니다. 따라서 객체를 사용하여 객체를 반복하려고 하면 열거 가능한 속성 속성이 false 인 경우 임의의 속성이 누락 될 수 있습니다. 특정 요소가 열거되지 않도록 일반 Array 객체의 열거 가능한 속성 속성을 변경하는 것이 가능합니다. 일반적으로 속성 속성은 객체 내의 함수 속성에 적용되는 경향이 있습니다. for...in

다음과 같은 방법으로 속성의 열거 가능한 속성 값을 확인할 수 있습니다.

myobject.propertyIsEnumerable('myproperty')

또는 네 가지 속성 속성을 모두 얻으려면

Object.getOwnPropertyDescriptor(myobject,'myproperty')

이는 ECMAScript 5에서 사용할 수있는 기능입니다. 이전 버전에서는 열거 가능한 속성 속성의 값을 변경할 수 없었습니다 (항상 true로 설정 됨).


8

for/ in해시 테이블 (연관 배열)과 어레이 (비 결합) 두 변수의 종류와 작동한다.

JavaScript는 항목을 통과하는 방식을 자동으로 결정합니다. 따라서 배열이 실제로 비 연관적임을 알고 있으면을 사용 for (var i=0; i<=arrayLen; i++)하고 자동 감지 반복을 건너 뛸 수 있습니다 .

그러나 제 생각에는 for/ 를 사용하는 것이 좋습니다in 자동 감지에 필요한 프로세스는 매우 작습니다.

이에 대한 진정한 대답은 브라우저가 JavaScript 코드를 파서 / 해석하는 방법에 달려 있습니다. 브라우저간에 변경 될 수 있습니다.

나는 for/를 사용하지 않는 다른 목적을 생각할 수 없다 in.

//Non-associative
var arr = ['a', 'b', 'c'];
for (var i in arr)
   alert(arr[i]);

//Associative
var arr = {
   item1 : 'a',
   item2 : 'b',
   item3 : 'c'
};

for (var i in arr)
   alert(arr[i]);

프로토 타입 객체를 사용하지 않는 한 true입니다. ;) 아래
Ricardo

그게 때문에이 Array있다 Object너무
무료 컨설팅

2
for ... in객체와 함께 작동합니다. 자동 감지와 같은 것은 없습니다.
더 나은 올리버

7

주의하지 않으면 프로토 타입 체인의 객체에 속하는 속성을 반복하기 때문입니다.

사용할 수 있습니다 . hasOwnProperty로for.. in 각 속성을 확인하십시오 .


2
충분하지 않습니다 - 그것은 배열 인스턴스에 임의의 명명 된 속성을 추가 할 수 있도록 완벽하게 확인, 그리고 그 테스트됩니다 true에서 hasOwnProperty()확인합니다.
Pointy

좋은 지적입니다. 감사합니다. 나는 배열에 스스로 그렇게 할 정도로 어리석은 적이 없기 때문에 그것을 고려하지 않았습니다!
JAL

1
@Pointy 나는 이것을 테스트하지는 않았지만 아마도 isNaN각 속성 이름을 확인 하여 극복 할 수 있습니다 .
WynandB

1
@Wynand 재미있는 아이디어; 그러나 간단한 숫자 색인으로 반복 할 때 왜 문제가 가치가 있는지 알지 못합니다.
Pointy

@WynandB 범프에 대해 죄송하지만 수정이 필요하다고 생각 isNaN합니다. 변수가 특수 값 NaN인지 여부를 확인하기위한 것입니다. '숫자 이외의 것'을 확인하는 데 사용할 수 없습니다 (정기적으로 갈 수 있음) 그에 대한 typeof).
doldt

6

그것은 아니에요 반드시 (당신이 무슨 일을하는지에 따라) 나쁜,하지만 뭔가가 추가 된 경우 배열의 경우 Array.prototype, 당신은 이상한 결과를 얻을 것입니다. 이 루프가 세 번 실행될 것으로 예상되는 위치 :

var arr = ['a','b','c'];
for (var key in arr) { ... }

라는 함수가있는 경우 helpfulUtilityMethod에 추가 된 ArrayS ' prototype, 다음 루프 네 번 실행 끝낼 것 : key0, 1, 2,와 helpfulUtilityMethod. 정수 만 기대하면 웁스.


6

for(var x in y)위에서 설명한 것처럼 객체가 아닌 속성 목록에서만 사용해야합니다 .


13
주석은 페이지의 주석 순서가 항상 바뀌므로 '위'가 없습니다. 따라서 우리는 당신이 어떤 의견을 의미하는지 잘 모릅니다. 이런 이유로 "x 사람의 의견으로"말하는 것이 좋습니다.
JAL

@JAL ... 또는 답변에 영구 링크를 추가하십시오.
WynandB

5

for...in왜 누군가가 당신에게 그렇게 말했는지 추측 할 수 있지만 배열에 루프를 사용하는 것은 잘못이 아닙니다.

1.) 배열의 목적을 가지고 있지만 'forEach'라는 더 많은 구문과 구문을 가진 고차 함수 또는 메소드가 이미 있습니다. Array.prototype.forEach(function(element, index, array) {} );

2) 배열은 항상 길이를 가지고 있지만, for...in그리고 forEach어떤 값을 함수를 실행하지 않는 'undefined'경우에만 정의 된 값이 인덱스에 대해. 따라서 하나의 값만 할당하면 이러한 루프는 함수를 한 번만 실행하지만 배열이 열거되므로 항상 정의 된 값을 갖는 가장 높은 색인까지의 길이를 갖지만 이러한 값을 사용할 때 해당 길이는 눈에 띄지 않을 수 있습니다 루프.

3.) 표준 for 루프는 매개 변수에서 정의한 횟수만큼 함수를 실행하며 배열에 번호가 매겨 지므로 함수를 실행할 횟수를 정의하는 것이 더 합리적입니다. 다른 루프와 달리 for 루프는 값의 정의 여부에 관계없이 배열의 모든 인덱스에 대해 함수를 실행할 수 있습니다.

본질적으로 모든 루프를 사용할 수 있지만 작동 방식을 정확하게 기억해야합니다. 서로 다른 루프가 반복되는 조건과 개별 기능을 이해하고 서로 다른 시나리오에 다소 적합하다는 것을 인식하십시오.

또한 작성하기 쉽고 기능이 더 많기 때문에 일반적으로 루프 forEach보다 메소드 를 사용하는 것이 더 나은 방법으로 간주 될 수 for...in있으므로이 메소드와 표준 만 사용하는 습관을 갖고 싶을 수도 있습니다. 요구.

아래의 첫 두 루프는 console.log 문을 한 번만 실행하는 반면, for 루프 표준은 지정된 횟수만큼 함수를 실행합니다 (이 경우 array.length = 6).

var arr = [];
arr[5] = 'F';

for (var index in arr) {
console.log(index);
console.log(arr[index]);
console.log(arr)
}
// 5
// 'F'
// => (6) [undefined x 5, 6]

arr.forEach(function(element, index, arr) {
console.log(index);
console.log(element);
console.log(arr);
});
// 5
// 'F'
// => Array (6) [undefined x 5, 6]

for (var index = 0; index < arr.length; index++) {
console.log(index);
console.log(arr[index]);
console.log(arr);
};
// 0
// undefined
// => Array (6) [undefined x 5, 6]

// 1
// undefined
// => Array (6) [undefined x 5, 6]

// 2
// undefined
// => Array (6) [undefined x 5, 6]

// 3
// undefined
// => Array (6) [undefined x 5, 6]

// 4
// undefined
// => Array (6) [undefined x 5, 6]

// 5
// 'F'
// => Array (6) [undefined x 5, 6]

4

이것이 (보통) 나쁜 습관 인 이유는 다음과 같습니다.

  1. for...in루프는 자체 열거 가능한 모든 속성 프로토 타입의 열거 가능한 속성을 반복 합니다. 일반적으로 배열 반복에서는 배열 자체 만 반복하려고합니다. 배열에 아무것도 추가하지 않아도 라이브러리 나 프레임 워크에 무언가가 추가 될 수 있습니다.

:

Array.prototype.hithere = 'hithere';

var array = [1, 2, 3];
for (let el in array){
    // the hithere property will also be iterated over
    console.log(el);
}

  1. for...in루프 는 특정 반복 순서를 보장하지 않습니다 . 요즘은 대부분의 최신 브라우저에서 일반적으로 주문이 표시되지만 100 % 보장은 없습니다.
  2. for...in루프는 undefined배열 요소, 즉 아직 할당되지 않은 배열 요소를 무시 합니다.

예 : :

const arr = []; 
arr[3] = 'foo';   // resize the array to 4
arr[4] = undefined; // add another element with value undefined to it

// iterate over the array, a for loop does show the undefined elements
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

console.log('\n');

// for in does ignore the undefined elements
for (let el in arr) {
    console.log(arr[el]);
}


2

for ... in 은 JavaScript에서 객체를 작업 할 때 유용하지만 Array에서는 유용하지 않지만 여전히 잘못된 방법이라고 말할 수는 없지만 권장되지는 않습니다 .for ... in 루프를 사용하여 아래 예제를 살펴보십시오 .

let txt = "";
const person = {fname:"Alireza", lname:"Dezfoolian", age:35}; 
for (const x in person) {
    txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

자 이제 Array로 해봅시다 :

let txt = "";
const person = ["Alireza", "Dezfoolian", 35]; 
for (const x in person) {
   txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

결과가 동일하게 보이면 ...

그러나에의 프로토 타입 뭔가하자의 뭔가를 해보자 배열을 ...

Array.prototype.someoneelse = "someoneelse";

이제 새 Array ()를 만듭니다.

let txt = "";
const arr = new Array();
arr[0] = 'Alireza';
arr[1] = 'Dezfoolian';
arr[2] = 35;
for(x in arr) {
 txt += arr[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 someoneelse

당신은 someoneelse를 참조하십시오 !!! ... 우리는 실제로이 경우 새로운 Array 객체를 반복합니다!

그것이 우리가 사용해야하는 이유 중 하나입니다 그래서 for..in을 신중하게, 그러나 항상 그런 것은 아니다 ...


2

for ... in 루프는 항상 키를 열거합니다. 객체 속성 키는 배열의 인덱스 속성을 포함하여 항상 문자열입니다.

var myArray = ['a', 'b', 'c', 'd'];
var total = 0
for (elem in myArray) {
  total += elem
}
console.log(total); // 00123


0

특히이 문제가 해결되지는 않았지만, 나는 아주 좋은 이유가와에 대한 ... 지금까지 사용하지가 있음을 추가 할 NodeList하나가에서 얻는 것처럼 ( querySelectorAll대신, 모든에서 반환 된 요소가 표시되지 않는 한, 전화 NodeList 특성 만 반복합니다.

단일 결과의 경우 다음을 얻었습니다.

var nodes = document.querySelectorAll(selector);
nodes
 NodeList [a._19eb]
for (node in nodes) {console.log(node)};
VM505:1 0
VM505:1 length
VM505:1 item
VM505:1 entries
VM505:1 forEach
VM505:1 keys
VM505:1 values

왜 내가 for (node in nodes) node.href = newLink;실패 했는지 설명했다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.