이미 알아보신대로, 객체를 for...in
으로 순회 시, 프로퍼티 등록 순서를 보장하지 않습니다. 이것은 전적으로 JS 엔진 구현에 따르며, 엔진 내부적으로 순서를 정해 두었다 하더라도 JS 공식적인 문서상으로 arbitrary
하다고 명시하고 있습니다.
참고로 현재 ECMAScript 2015를 따라 구현된 JS 엔진에서는 string
과 Symbol
로 추가된 속성에 대해서는 그 순서를 보장하고 있습니다.
그래도 몇몇 이슈가 있긴 해서(정수형 인덱스와 혼합된 객체의 순서 보장 문제, delete 부작용)
등록된 순서를 보장되는 자료구조를 필요로 한다면 대체재로 Map
을 쓰고 for...of
로 순회하는 것을 권하고 있습니다.
P.S.1
JS에서 array-like 객체라고 하면,
속성으로 length
가 있고 개별 값으로 정수형 인덱스 접근이 가능하나
Array의 프로토타입 메소드들이 존재하지 않는 것을 말합니다.
(이 설명도 정확한건 아닙니다만… 아무튼,)
JS 프로그래밍 과정중에서 종종 보게 될 array-like 객체는
보통 정수로 인덱싱 되어 있으므로
for (var i = 0; i < array_like.length; i++)
문으로 인덱스 순서를 보장받을 수 있겠죠.
만약 해당 array-like 객체가 이터러블 프로토콜을 만족시킨다면
(e.g. arguments, HTMLCollection, NodeList)
for...of
로도 순회가 가능할겁니다.
임의로 만든 array-like 객체를 보시면,
const a = {};
a.prop2 = 'prop2';
a.prop1 = 'prop1';
a[3] = 3;
a[1] = 1;
a[0] = 0;
a.length = 4;
for (let p in a)
console.log(p);
/*
0
1
3
prop2
prop1
length
*/
string
타입의 키 값은 넣는 순서를 보장합니다.
정수형 키 값은 오름차순으로 정렬되며 순회 시 string
형 보다 먼저 접근됩니다.
(주의: 경우에 따라 아닐 수도 있습니다.)
여기에 iterator
프로토콜을 만족하도록 Symbol
속성을 추가하면
마치 배열인 것처럼 for...of
로 순회 가능하며 인덱스 순서를 보장받을 수 있습니다.
정수형 속성이 아닌 값들은 무시되고요.
// iterator 구현까지는 좀 귀찮으므로 Array의 iterator를 빌려 쓰자.
a[Symbol.iterator] = Array.prototype[Symbol.iterator];
for (let v of a)
console.log(v);
/*
0
1
undefined
3
*/
질문에서 언급하신
는 제가 이해를 잘못한 것이 아니라면
문맥상으로 오해의 소지가 있는 표현 같습니다.
P.S.2
JS에서 배열은 연속된 메모리에서 특정 주소값으로부터의 offset으로 값을 접근할 수 있는 자료구조가 아닙니다. (그것과 유사한 개념을 원하신다면 TypedArray를 살펴 보세요.)
K-V 컬렉션으로 이해하시는게 오히려 쉬울 수 있습니다.
JS의 Array 특징과 관련하여(정수형 인덱스와 Array.length
와의 관계, empty slot, 등등) 이 곳 jsdev에서 한 차례 논의 된 바 있었습니다. 아래 링크를 참고해 보세요.