JavaScript는 객체 속성 순서를 보장합니까?


647

이런 객체를 만들면 :

var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";

결과 객체는 항상 이런 모양입니까?

{ prop1 : "Foo", prop2 : "Bar" }

즉, 속성은 내가 추가 한 순서와 동일합니까?





1
@TJCrowder 허용되는 답변이 더 이상 정확하지 않은 이유에 대해 좀 더 자세히 설명해 주시겠습니까? 당신이 연결 한 질문은 속성 순서가 사양에 따라 여전히 보장되지 않는다는 생각으로 요약됩니다.
zero298

2
@ zero298 : 해당 질문에 대한 답변 은 ES2015 + 기준으로 지정된 속성 순서를 명확하게 설명합니다 . 레거시 작업 ( for-in, Object.keys) 은이를 공식적으로 지원할 필요 없지만 지금 주문이 있습니다. (비공식적 : 파이어 폭스, 크롬, 그리고 Edge는 모든 심지어 그들이 공식적으로 필요하지 않습니다 Object.keys,-에 대한 및에 지정된 순서를 따르 jsfiddle.net/arhbn3k2/1 )
TJ 크라우에게

답변:


474

객체의 반복 순서는 ES2015 이후 특정 규칙 세트를 따르지만 항상 삽입 순서를 따르지 않습니다 . 간단히 말해서, 반복 순서는 문자열 키의 삽입 순서와 숫자와 같은 키의 오름차순의 조합입니다.

// key order: 1, foo, bar
const obj = { "foo": "foo", "1": "1", "bar": "bar" }

배열이나 Map객체를 사용하는 것이 더 좋은 방법 일 수 있습니다. Map주 일부 유사점 Object삽입의 순서로 반복되는 키를 보장 예외없이 :

맵에있는 키는 객체에 추가 된 키가 아닌 순서로 정렬됩니다. 따라서 반복 할 때 Map 객체는 삽입 순서대로 키를 반환합니다. (ECMAScript 2015 사양 객체는 문자열 및 심볼 키의 생성 순서를 유지하므로 문자열 키만있는 객체의 순회는 삽입 순서에 따라 키를 생성합니다)

참고로 ES2015 이전에는 객체의 속성 순서가 전혀 보장되지 않았습니다. ECMAScript Third Edition 의 객체 정의 (pdf) :

4.3.3 객체

객체는 Object 유형의 멤버입니다. 순서가없는 속성 모음으로 각각 기본 값, 객체 또는 함수를 포함합니다. 객체의 속성에 저장된 함수를 메서드라고합니다.


정수 키의 동작이 모든 브라우저에서 일관되지는 않습니다. 일부 오래된 브라우저는 정수 키를 삽입 순서 (문자열 키 포함)로 반복하고 일부는 오름차순으로 반복합니다.
Dave Dopson

1
@DaveDopson-오른쪽-사용되지 않는 브라우저는 업데이트되지 않으므로 현재 사양을 따르지 않습니다.
TJ Crowder

199

예 (정수가 아닌 키의 경우).

대부분의 브라우저는 다음과 같이 객체 속성을 반복합니다.

  1. 오름차순의 정수 키 (및 int로 구문 분석하는 "1"과 같은 문자열)
  2. 삽입 순서의 문자열 키
  3. 삽입 순서의 기호 이름 (ES2015는이를 준수하며 모든 브라우저는이를 준수 함)

일부 구형 브라우저는 카테고리 # 1과 # 2를 결합하여 모든 키를 삽입 순서대로 반복합니다. 키가 정수로 구문 분석 될 수있는 경우 특정 반복 순서에 의존하지 않는 것이 가장 좋습니다.

브라우저마다 동작이 달라지는 정수 (예 : "7"또는 "99")로 구문 분석되는 키의 경우를 제외하고 현재 언어 사양 (ES2015 이후) 삽입 순서가 유지됩니다. 예를 들어, 키가 숫자로 구문 분석 될 때 Chrome / V8은 삽입 순서를 고려하지 않습니다.

이전 언어 사양 (ES2015 이전) : 반복 순서는 기술적으로 정의되지 않았지만 모든 주요 브라우저는 ES2015 동작을 준수했습니다.

ES2015 동작은 기존 동작에 의해 구동되는 언어 사양의 좋은 예이며 다른 방식은 아닙니다. 이전 버전과의 호환성에 대한 심도를 더 깊이 이해하려면 Chrome의 반복 순서 동작에 대한 디자인 결정을 자세히 설명하는 Chrome 버그 인 http://code.google.com/p/v8/issues/detail?id=164를 참조하십시오 . . 해당 버그 보고서에 대한 (의견이없는) 의견 중 하나에 대해 :

표준은 항상 XHR의 출처 인 구현을 따르며 Google은 Gears를 구현 한 다음 동등한 HTML5 기능을 수용함으로써 동일한 작업을 수행합니다. 올바른 수정은 ECMA가 사실상의 표준 동작을 사양의 다음 개정판에 공식적으로 통합하도록하는 것입니다.


2
@ BenjaminGruenbaum-정확히 내 요점이었다. 2014 년 현재 모든 주요 공급 업체는 공통으로 구현되었으므로 표준은 결국 (2015 년) 준수 될 것입니다.
Dave Dopson

2
그건 그렇고 : React createFragmentAPI는 이미 이것에 의존합니다 ... 🤔
mik01aj

7
@BenjaminGruenbaum 댓글이 거짓입니다. ES2015에서는 선택된 방법에 대해서만 순서가 보장됩니다 . 아래 ftor의 답변 을 참조하십시오 .
Piotr Dobrogost

82

일반 객체의 속성 순서는 Javascript의 복잡한 주제입니다.

ES5에서는 명시 적으로 주문이 지정되어 있지 않지만 ES2015는 특정 주문이 있습니다. 다음과 같은 객체가 주어진다 :

o = Object.create(null, {
  m: {value: function() {}, enumerable: true},
  "2": {value: "2", enumerable: true},
  "b": {value: "b", enumerable: true},
  0: {value: 0, enumerable: true},
  [Symbol()]: {value: "sym", enumerable: true},
  "1": {value: "1", enumerable: true},
  "a": {value: "a", enumerable: true},
});

결과는 다음과 같습니다 (특정 경우).

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}
  1. 정수형 키 (오름차순)
  2. 삽입 순서의 일반 키
  3. 삽입 순서의 기호

따라서 삽입 순서를 변경할 수있는 세 개의 세그먼트가 있습니다 (예제에서와 같이). 그리고 정수와 같은 키는 삽입 순서에 전혀 영향을 미치지 않습니다.

문제는 ES2015 사양 에서이 순서가 보장되는 방법은 무엇입니까?

다음 방법은 표시된 순서를 보장합니다.

  • Object.assign
  • Object.defineProperties
  • Object.getOwnPropertyNames
  • Object.getOwnPropertySymbols
  • Ref.ownKeys

다음 방법 / 루프는 전혀 순서를 보장하지 않습니다.

  • Object.keys
  • for..in
  • JSON.parse
  • JSON.stringify

결론 : ES2015에서도 Javascript에서 일반 객체의 속성 순서에 의존해서는 안됩니다. 오류가 발생하기 쉽습니다. Map대신 사용하십시오 .


노드 v8.11의 결론을 대략적으로 테스트했으며 정확합니다.
merlin.ye

1
@BenjaminGruenbaum 어떤 생각?
evolutionxbox

정수 규칙에 따라 Object.entries는 순서를 보장합니다
Mojimi

1
"ES2015에서도 Javascript에서 일반 객체의 속성 순서에 의존해서는 안됩니다. 오류가 발생하기 쉽습니다. 대신 맵을 사용하십시오." 규정 준수가 복잡 할 때는 부동산 주문에 의존 할 수 없습니다. 구형 브라우저를 지원해야하는 경우 이전 버전과의 비교 가능성을 어떻게 테스트합니까?
zero298

1
그것에 대한 공식 문서 또는 참조가 있습니까?
비트

66

작성 당시 대부분의 브라우저는 삽입 된 순서와 동일한 순서로 속성을 반환했지만 동작이 명시 적으로 보장되지는 않았으므로 의존해서는 안됩니다.

인 ECMAScript 사양은 말을 사용 :

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

그러나 ES2015 이상에서는 정수가 아닌 키가 삽입 순서대로 반환됩니다.


16
Chrome은 다른 브라우저와 다른 순서를 구현합니다. 참조 code.google.com/p/v8/issues/detail?id=164
팀 다운을

9
IE9뿐만 아니라 Opera 10.50 이상은 Chrome의 주문과 일치합니다. Firefox와 Safari는 이제 소수입니다 (둘 다 객체 / 배열에 대해 다른 순서를 사용합니다).
gsnedders

1
@Veverke는 명시 적으로 주문에 대한 보증이 없으므로 주문이 효과적으로 무작위라고 가정해야합니다.
Alnitak

1
@Veverke 아니오, 순서는 예측할 수있는 것과 다릅니다. 구현에 따라 다르며 언제든지 변경 될 수 있으며 브라우저가 자체적으로 업데이트 될 때마다 변경 될 수 있습니다 (예 :).
Alnitak

3
이 답변은 ES2015에서 거짓입니다.
Benjamin Gruenbaum

42

이 전체 답변은 특정 순간이나 역사적으로 엔진이하는 것이 아니라 사양 준수의 맥락에 있습니다.

일반적으로

실제 질문은 매우 모호합니다.

속성을 내가 추가 한 순서와 동일하게

어떤 맥락에서?

답은 여러 가지 요소에 따라 다릅니다. 일반적으로, 전혀 .

어쩔 땐 그래

다음은 평범한 속성 키 순서를 신뢰할 수있는 위치입니다 Objects.

  • ES2015 호환 엔진
  • 자신의 속성
  • Object.getOwnPropertyNames(), Reflect.ownKeys(),Object.getOwnPropertySymbols(O)

모든 경우에 이러한 방법에는 열거 할 수없는 속성 키와 주문 키가 포함됩니다 [[OwnPropertyKeys]](아래 참조). 포함하는 키 값의 유형 ( String및 / 또는 Symbol) 이 다릅니다 . 이와 관련하여 String정수 값 이 포함됩니다.

Object.getOwnPropertyNames(O)

O자체 String키 속성 ( 속성 이름 )을 반환 합니다.

Reflect.ownKeys(O)

O자체 StringSymbol키 속성을 반환 합니다.

Object.getOwnPropertySymbols(O)

O자체 Symbol키 속성을 반환 합니다.

[[OwnPropertyKeys]]

순서는 본질적 Strings으로 오름차순의 정수와 같고 Strings, 생성 순서의 정수와 같지 않고 , 생성 순서의 기호 와 같습니다 . 이를 호출하는 함수에 따라 이러한 유형 중 일부는 포함되지 않을 수 있습니다.

특정 언어는 키가 다음 순서로 반환된다는 것입니다.

  1. ... 정수 색인 PO[반복되는 객체] 의 고유 한 속성 키 , 숫자 인덱스 순서 오름차순

  2. ... 각각 자신의 속성 키 PO그 문자열하지만 속성 생성하기, 정수 인덱스가 아닙니다

  3. ... 각 고유의 속성 키 PO속성 생성 순서에서 기호입니다.

Map

정렬 된 맵에 관심이있는 경우 Mapplain 대신 ES2015에 도입 된 유형을 사용하는 것이 좋습니다 Objects.


13

최신 브라우저에서는 Map객체 대신 데이터 구조를 사용할 수 있습니다 .

개발자 mozilla>지도

Map 객체는 삽입 순서대로 요소를 반복 할 수 있습니다 ...


7

ES2015에서는 생각하지만 그렇게 생각하지 않습니다

ES2015까지는 객체의 키 순서가 보장되지 않았습니다. 구현 정의되었습니다.

그러나, ES2015의에 있었다 지정되었습니다. JavaScript의 많은 것들과 마찬가지로, 이것은 호환성을 목적으로 수행되었으며 일반적으로 대부분의 JS 엔진에있는 기존의 비공식 표준을 반영합니다 (예외를 아는 사람).

순서는 추상 조작 OrdinaryOwnPropertyKeys 아래의 스펙에 정의되어 오브젝트 자체의 키를 반복하는 모든 메소드를 뒷받침합니다. 해석하면 순서는 다음과 같습니다.

  1. 모두 정수 인덱스 키 (같은 물건 "1123", "55"숫자 오름차순으로, 등).

  2. 생성 순서대로 정수 인덱스가 아닌 모든 문자열 키 (가장 오래된 것부터).

  3. 생성 순서에 따른 모든 기호 키 (가장 오래된 것부터).

순서가 신뢰할 수 없다고 말하는 것은 어리석은 일입니다. 신뢰할 수 있고, 아마도 원하는 것이 아니며, 최신 브라우저는이 순서를 올바르게 구현합니다.

일부 예외는 for .. in루프 와 같은 상속 된 키를 열거하는 방법을 포함합니다 . for .. in루프는 사양에 따라 순서를 보장하지 않습니다.


7

ES2015부터는 속성을 반복하는 특정 방법에 대해 속성 순서가 보장됩니다. 그러나 다른 사람은 아닙니다 . 불행히도, 주문을 보장하지 않는 방법이 일반적으로 가장 많이 사용됩니다.

  • Object.keys, Object.values,Object.entries
  • for..in 루프
  • JSON.stringify

그러나 ES2020부터는 이전에 신뢰할 수 없었던 방법의 속성 순서 것이다 스펙으로 보장 할 수 인해에에, 다른 사람과 같은 결정 론적 방식으로 이상 반복 할 수 완성 제안 : 대한-에서 역학 .

마찬가지로 (보장 된 반복 순서를 가지고있는 방법과 같은 Reflect.ownKeysObject.getOwnPropertyNames ) 메소드 이전에 지정되지 않은 메소드도 다음 순서로 반복됩니다.

  • 숫자 배열 키 (오름차순)
  • 삽입 순서대로 다른 모든 비 기호 키
  • 삽입 순서의 기호 키

이것은 거의 모든 구현이 이미 (그리고 수년 동안 해왔 던) 일이지만, 새로운 제안은 그것을 공식적으로 만들었습니다.

현재 사양은 반복 순서대로 " 거의 완전히 지정되지 않았지만 실제 엔진은 더 일관된 경향이 있습니다."

ECMA-262의 특이성 부족은 현실을 반영하지 않습니다. 몇 년 전 토론에서 구현 자들은 웹에서 코드를 실행하려는 사람이 따라야하는 for-in의 동작에 일부 제약이 있음을 관찰했습니다.

모든 구현은 이미 예측 가능한 속성을 반복하기 때문에 이전 버전과의 호환성을 유지하면서 사양에 넣을 수 있습니다.


현재 구현에 동의 하지 않는 몇 가지 이상한 경우가 있으며 이러한 경우 결과 순서는 지정되지 않습니다. 부동산 주문 이 보장 되려면 :

반복되는 객체 나 프로토 타입 체인의 어떤 것도 프록시, 형식화 된 배열, 모듈 네임 스페이스 객체 또는 호스트 이국적인 객체가 아닙니다.

프로토 타입 체인의 객체 나 어떤 것도 반복하는 동안 프로토 타입이 변경되지 않습니다.

객체 나 프로토 타입 체인의 어떤 것도 반복하는 동안 속성이 삭제되지 않습니다.

객체의 프로토 타입 체인에는 반복 중에 추가 된 속성이 없습니다.

프로토 타입 체인의 객체 또는 기타 객체의 속성은 반복 동안 열거 성이 변경되지 않습니다.

열거 할 수없는 속성 그림자는 열거 할 수 없습니다.


5

다른 사람들이 언급했듯이 객체의 속성을 반복 할 때 순서에 대해 보장하지 않습니다. 여러 필드의 순서가 지정된 목록이 필요한 경우 객체 배열을 만드는 것이 좋습니다.

var myarr = [{somfield1: 'x', somefield2: 'y'},
{somfield1: 'a', somefield2: 'b'},
{somfield1: 'i', somefield2: 'j'}];

이런 식으로 정규 for 루프를 사용하고 삽입 순서를 가질 수 있습니다. 그런 다음 Array sort 메소드를 사용하여 필요한 경우 새 배열로 정렬 할 수 있습니다.


3

이 방법이 어려운 것을 알았습니다.

Redux와 함께 React를 사용하면 하위를 생성하기 위해 통과하려는 키의 상태 컨테이너가 저장소가 변경 될 때마다 (Reux의 불변성 개념에 따라) 새로 고쳐집니다.

따라서을 Object.keys(valueFromStore)사용 하기 위해 Object.keys(valueFromStore).sort()적어도 키의 알파벳 순서를 갖도록 사용했습니다.


-7

로부터 JSON 표준 :

객체는 0 개 이상의 이름 / 값 쌍으로 정렬되지 않은 컬렉션입니다. 여기서 이름은 문자열이고 값은 문자열, 숫자, 부울, null, 객체 또는 배열입니다.

(강조 광산).

따라서 주문을 보장 할 수는 없습니다.


7
이것은 JSON 사양이 아닌 ECMAScript 표준에 의해 지정됩니다.
Alnitak

7
@Alnitak, @Iacqui : JSON은 이것을 ECMAScript 사양에서만 가져옵니다. JSON에도 지정되었지만 실제로 질문과 관련이 없습니다.
Paŭlo Ebermann
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.