Sắp xếp các thuộc tính trong Javascript bị hỏng


15

Tôi cần lặp qua một đối tượng JavaScript coi nó như một mảng với các khóa tùy chỉnh. Tôi biết điều này không được hỗ trợ đầy đủ, vì các thuộc tính không có thứ tự đầu ra, nhưng vì tôi luôn sắp xếp lại các thuộc tính, tôi thấy cách tiếp cận này đơn giản và đáng tin cậy ... cho đến bây giờ.

Sự cố xảy ra khi các khóa là số hoặc chuỗi có thể được đúc thành số.

Khi tôi chạy mã này:

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);}

đầu ra là:

4294966111
4294966222
4294966333
---
4294968222
4294968333
4294968111

Nghĩa là:

  • (test1) nếu các khóa dưới 2 ^ 32 (4.294.967.296), chúng sẽ tự động được sắp xếp lại, nhỏ nhất trước
  • (test2) nếu các khóa trên 2 ^ 32, chúng KHÔNG được sắp xếp lại.

Câu hỏi là: tại sao điều này xảy ra?

Vì tất cả các trình duyệt tôi đã kiểm tra (Google Chrome 79.0, Mozilla Firefox 71.0, Microsoft Edge 44.18362, Internet Explorer 11.535) đồng ý về đầu ra này, nên phải có một số thông số kỹ thuật chính thức.

Cập nhật

Tôi đã thử nghiệm rất nhiều con số trước khi phát hiện ra đó là một vấn đề ngưỡng. Tôi thấy kỳ lạ là chuỗi 2,3,1 hoạt động khác với ba dấu thời gian được sắp xếp theo cùng một cách.


Tôi đoán là cách tính mã băm, nhưng nó không phải là câu trả lời thực sự cho câu hỏi của bạn.
Mario Vernari

1
Tôi không nghĩ nó bị hỏng theo nghĩa thông tục thực sự của từ này, họ không đảm bảo rằng các giá trị sẽ được lặp theo thứ tự vì nó chạy tùy ý khi bạn có thể kiểm tra developer.mozilla.org/en-US/docs/Web / JavaScript / Reference / Long "Lưu ý: for ... in không nên được sử dụng để lặp qua một mảng trong đó thứ tự chỉ mục là quan trọng." Họ chỉ đảm bảo lặp đi lặp lại trên mọi yếu tố trong bộ sưu tập. Một cái gì đó giống như forEach thực sự tính đến thứ tự bằng cách duyệt qua các mục theo thứ tự tăng dần ecma
i Intl.org/ecma-262/5.1/#sec-15.4.4.18

Đối với bản ghi, bạn có thể thấy vấn đề trực tiếp bằng cách đăng nhập test1test2. Tôi nghĩ rằng "vấn đề" là từ bộ nhớ đệm chính trong việc triển khai V8 của thông số kỹ thuật.
Sablor

Nhiều hơn là dưới 2 ^ 32 tên tài sản của bạn được sắp xếp ngẫu nhiên tương tự như các tham chiếu thuộc tính nội bộ. Bạn có thể và không nên dựa vào thứ tự của các thuộc tính đối tượng vì theo định nghĩa chúng không được sắp xếp và có thể chứa các thuộc tính vốn là đối tượng. Luôn truyền / ánh xạ đối tượng của bạn thành một mảng, sắp xếp mảng, sau đó lặp qua nó nếu thứ tự là quan trọng.
dùng3154108

1
@ Mr.Toxy Đó là vì những tính chất đó 42949683334294968111lớn hơn 2 ** 32(đó là 4294967296). Vì vậy, chúng không phải là chỉ số mảng, vì vậy chúng được lặp đi lặp lại theo thứ tự tạo tài sản, thay vì theo thứ tự số tăng dần - đó chính xác là những gì chúng đang làm trong câu đố, như mong đợi. (xem câu trả lời của tôi)
SurePerformance

Câu trả lời:


4

Điều này được mong đợi. Theo đặc tả , phương thức lặp qua các thuộc tính OrdinaryOwnPropertyKeys, thực hiện:

  1. Đối với mỗi khóa thuộc tính riêng P của O là một chỉ mục mảng , theo thứ tự chỉ số số tăng dần, hãy làm

    a. Thêm P là phần tử cuối cùng của khóa.

  2. Đối với mỗi khóa thuộc tính riêng P của O là một Chuỗi nhưng không phải là một chỉ mục mảng, theo thứ tự thời gian tăng dần của việc tạo thuộc tính, hãy làm

    a. Thêm P là phần tử cuối cùng của khóa.

Thứ tự số tăng dần chỉ áp dụng cho các thuộc tính là chỉ thị mảng.

Vậy, "chỉ số mảng" là gì? Tra cứu nó ::

Chỉ số nguyên là khóa thuộc tính có giá trị Chuỗi là Chuỗi số chính tắc (xem 7.1.21) và có giá trị số là +0 hoặc số nguyên dương ≤ 2 ^ 53 - 1. Chỉ mục mảng là chỉ số nguyên có số nguyên giá trị i nằm trong phạm vi +0 i <2 ^ 32 - 1.

Vì vậy, các thuộc tính số lớn hơn 2 ^ 32 không phải là chỉ số mảng và do đó được lặp theo thứ tự tạo thuộc tính. Tuy nhiên, các thuộc tính số nhỏ hơn 2^32 chỉ số mảng và được lặp lại theo thứ tự số tăng dần.

Ví dụ:

1: Chỉ mục mảng, sẽ được lặp lại qua số

10: Chỉ mục mảng, sẽ được lặp lại qua số

4294968111: Lớn hơn 2 ** 32, sẽ được lặp lại sau khi chỉ báo mảng kết thúc, theo thứ tự tạo thuộc tính

9999999999999: Lớn hơn 2 ** 32, sẽ được lặp lại sau khi chỉ báo mảng kết thúc, theo thứ tự tạo thuộc tính

Ngoài ra, hãy nhớ rằng, trái với niềm tin phổ biến, thứ tự lặp lại thuộc tính cũng được đảm bảo bởi thông số kỹ thuật, nhờ vào đề xuất lặp lại trong giai đoạn 4.


2

Điều này có liên quan đến cách các phím của đối tượng được duyệt qua.

Theo thông số kỹ thuật ES6, nó phải là:

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-i Intl.org/ecma-262/6.0/#sec-iverse-object-iternal-methods-and-iternal-slots-ownpropertykeys

Điều đó có nghĩa là nếu giá trị của khóa giữ nguyên nếu được chuyển đổi thành số 53 bit không dấu và ngược lại, nó được coi là một chỉ số nguyên được sắp xếp theo thứ tự số tăng dần.

Nếu điều này không thành công, nó được coi là một khóa chuỗi, được sắp xếp theo cách chúng được thêm vào đối tượng.

Điều hấp dẫn ở đây là tất cả các trình duyệt chính chưa tuân theo thông số kỹ thuật này và sử dụng một chỉ mục mảng thay vào đó bị giới hạn ở một số dương 2 ^ 32-1. Vì vậy, bất cứ điều gì trên giới hạn đó là một khóa chuỗi thực sự.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.