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

記録のために、ロギングtest1およびによって問題を直接確認できますtest2。「問題」は、仕様のV8実装のキーキャッシングにあると思います。
Seblor

2 ^ 32より下では、プロパティ名は内部プロパティ参照と同様に偶然に並べられます。オブジェクトプロパティの順序に依存することはできませんし、そうすべきではありません。定義上、オブジェクトプロパティは順序付けされておらず、オブジェクト固有のプロパティを含むことができるためです。オブジェクトを常に配列にキャスト/マップし、配列を並べ替えて、順序が重要な場合はそれをループします。
user3154108

1
@ Mr.Toxyこれは、これらのプロパティ429496833342949681112 ** 32(つまり4294967296)より大きいためです。したがって、これらは配列インデックスではないため、昇順の数値順ではなく、プロパティの作成順で繰り返されます。これは、実際にフィドルで行っていることとまったく同じです。(私の回答を参照してください)
特定のパフォーマンス

回答:


4

これは予想通りです。仕様ごと、プロパティを反復処理する方法は、OrdinaryOwnPropertyKeys、行います。

  1. 配列インデックスであるOの各プロパティキーPに対して、数値インデックスの昇順で、

    a。キーの最後の要素としてPを追加します。

  2. 文字列であるが配列インデックスではないOの各プロパティキーPについて、プロパティ作成の時系列の昇順で、次のようにします。

    a。キーの最後の要素として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.