Javascript의 정렬 속성이 깨졌습니다.


15

사용자 정의 키가있는 배열로 취급하는 JavaScript 객체를 반복해야합니다. 속성이 본질적인 순서가 없기 때문에 이것이 완전히 지원되지는 않는다는 것을 알고 있지만 항상 속성을 재정렬하기 때문에이 접근법은 간단하고 신뢰할 수 있습니다 ... 지금까지.

키가 숫자이거나 숫자로 캐스트 될 수있는 문자열 인 경우 문제가 발생합니다.

이 코드를 실행할 때 :

var test1 = {4294966222:"A",4294966333:"A",4294966111:"A"};
var test2 = {4294968222:"A",4294968333:"A",4294968111:"A"};
        
for (var k in test1) {console.log(k);}
console.log("---");
for (var k in test2) {console.log(k);}

출력은 다음과 같습니다

4294966111
4294966222
4294966333
---
4294968222
4294968333
4294968111

다음을 의미합니다.

  • (test1) 키가 2 ^ 32 (4,294,967,296) 미만인 경우 자동으로 순서가 가장 작은 것부터
  • (test2) 키가 2 ^ 32보다 높으면 순서가 변경되지 않습니다.

문제는 왜 이런 일이 일어나고 있습니까?

테스트 한 모든 브라우저 (Google Chrome 79.0, Mozilla Firefox 71.0, Microsoft Edge 44.18362, Internet Explorer 11.535)가이 출력에 동의하므로 공식적인 사양이 있어야합니다.

최신 정보

나는 그것이 임계 문제라는 것을 알기 전에 많은 숫자를 테스트했습니다. 시퀀스 2,3,1이 동일한 방식으로 정렬 된 3 개의 타임 스탬프와 다르게 동작한다는 것이 이상했습니다.


내 추측은 해시 코드가 계산되는 방법이지만 귀하의 질문에 대한 진정한 대답은 아닙니다.
마리오 베르 나리

1
나는 그것이 실제 구어 적 의미에서 깨 졌다고 생각하지 않으며, developer.mozilla.org/en-US/docs/Web 을 확인할 수 있기 때문에 임의로 실행되므로 값이 순서대로 반복된다고 보장하지 않습니다. / JavaScript / Reference /… "참고 : for ... in은 인덱스 순서가 중요한 배열을 반복하는 데 사용해서는 안됩니다." 컬렉션의 모든 요소에 대해서만 반복을 보장합니다. forEach는 오름차순으로 항목을 순회하여 주문을 실제로 고려합니다. ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
Mr.Toxy

기록을 위해, 당신은 로그인하여 직접 문제를 볼 수 있습니다 test1test2. "문제"는 스펙의 V8 구현에서 핵심 캐싱에서 비롯된 것으로 생각합니다.
Seblor

2 ^ 32 이하에서는 속성 이름이 내부 속성 참조와 유사하게 일치하게 정렬됩니다. 정의에 따라 정렬되지 않고 개체 고유의 속성을 포함 할 수 있으므로 개체 속성의 순서에 의존 할 수 있습니다. 객체를 항상 배열로 캐스트 / 매핑하고 배열을 정렬 한 다음 순서가 중요한 경우 반복합니다.
user3154108

1
@ Mr.Toxy 그의 이러한 속성 때문에 4294968333그리고 4294968111보다 큰 2 ** 32(이다 4294967296). 따라서 배열 인덱스는 아니므로 숫자 순서가 오름차순이 아니라 속성 생성 순서로 반복됩니다. 이는 정확히 바이올린에서 수행하는 것과 같습니다. (내 답변 참조)
특정 성능

답변:


4

이것은 예상됩니다. 당 사양 , 특성을 반복 처리 방법은 OrdinaryOwnPropertyKeys,하지 :

  1. 배열 인덱스 인 고유 속성 키 P의 O에 대해 오름차순 숫자 인덱스 순서로

    ㅏ. 키의 마지막 요소로 P를 추가하십시오.

  2. 문자열이지만 배열 인덱스가 아닌 O의 각 속성 키 P에 대해 시간 순서대로 속성 생성 순서에 따라

    ㅏ. 키의 마지막 요소로 P를 추가하십시오.

오름차순 숫자 순서 는 배열 인덱스 인 속성 에만 적용됩니다.

"어레이 인덱스"란 무엇입니까? 찾아보십시오 : :

정수 인덱스는 표준 숫자 문자열 (7.1.21 참조)이고 숫자 값이 +0 또는 양의 정수 ≤ 2 ^ 53-1 인 문자열 값 속성 키입니다. 배열 인덱스는 숫자의 정수 인덱스입니다. 값 i는 +0 ≤ i <2 ^ 32-1 범위에 있습니다.

따라서 2 ^ 32보다 큰 숫자 속성은 배열 인덱스 가 아니므 로 속성 생성 순서대로 반복됩니다. 그러나,보다 작은 수치 속성 2^32 이다 어레이 모든 인덱스 및 숫자의 오름차순에 걸쳐 반복된다.

예를 들어,

1: 배열 인덱스, 숫자로 반복됩니다

10: 배열 인덱스, 숫자로 반복됩니다

4294968111:보다 큼 2 ** 32, 속성 생성 순서에서 배열 인덱스가 완료된 반복됩니다.

9999999999999:보다 큼 2 ** 32, 속성 생성 순서에서 배열 인덱스가 완료된 반복됩니다.

또한, 일반적인 생각과는 달리, 재산 반복 순서가 있음을 염두에 두어야 한다 ,뿐만 아니라 사양에 의해 덕분에 보장 을위한-의 반복 제안 단계 4입니다.


2

이것은 객체의 키가 가로 지르는 방식과 관련이 있습니다.

ES6 사양에 따르면 다음과 같아야합니다.

9.1.12 [[OwnPropertyKeys]] ( )

When the [[OwnPropertyKeys]] internal method of O is called the following steps are taken:

    Let keys be a new empty List.
    For each own property key P of O that is an integer index, in ascending numeric index order
        Add P as the last element of keys.
    For each own property key P of O that is a String but is not an integer index, in property creation order
        Add P as the last element of keys.
    For each own property key P of O that is a Symbol, in property creation order
        Add P as the last element of keys.
    Return keys.

http://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys

즉, 부호없는 53 비트 숫자로 변환 된 키의 값이 동일하게 유지되면 다시 키 값이 오름차순으로 정렬 정수 인덱스 .

이것이 실패하면 문자열 키로 취급되며 개체에 추가 된 방식으로 정렬됩니다.

여기서 중요한 것은 모든 주요 브라우저가이 사양을 아직 따르지 않고 최대 양수로 제한되는 배열 인덱스를 대신 사용한다는 것 2 ^ 32-1입니다. 따라서이 제한을 초과하는 것은 실제로 문자열 키입니다.

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