JavaScript có đảm bảo đối tượng đặt hàng tài sản không?


647

Nếu tôi tạo một đối tượng như thế này:

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

Đối tượng sẽ luôn luôn trông như thế này?

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

Đó là, các thuộc tính sẽ theo thứ tự mà tôi đã thêm chúng?


1
có thể trùng lặp thứ tự
Borgar



1
@TJCrowder Bạn có thể đi vào chi tiết hơn một chút về lý do tại sao câu trả lời được chấp nhận không còn chính xác? Câu hỏi mà bạn liên kết dường như sôi nổi với ý tưởng rằng thứ tự tài sản vẫn không được đảm bảo cho mỗi thông số kỹ thuật.
zero298

2
@ zero298: Câu trả lời được chấp nhận cho câu hỏi đó mô tả rõ ràng thứ tự thuộc tính được chỉ định kể từ ES2015 +. Hoạt động Legacy ( for-in, Object.keys) không cần phải hỗ trợ nó (chính thức), nhưng có trật tự bây giờ. (Không chính thức: Firefox, Chrome, và Edge tất cả làm theo trình tự quy định ngay cả trong cho-in và Object.keys, nơi họ không được chính thức yêu cầu phải: jsfiddle.net/arhbn3k2/1 )
TJ Crowder

Câu trả lời:


474

Thứ tự lặp cho các đối tượng tuân theo một bộ quy tắc nhất định kể từ ES2015, nhưng nó không (luôn luôn) tuân theo thứ tự chèn . Nói một cách đơn giản, thứ tự lặp là sự kết hợp của thứ tự chèn cho các phím chuỗi và thứ tự tăng dần cho các phím giống như số:

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

Sử dụng một mảng hoặc một Mapđối tượng có thể là một cách tốt hơn để đạt được điều này. Mapchia sẻ một số điểm tương đồng với Objectđảm bảo các khóa được lặp theo thứ tự chèn , không có ngoại lệ:

Các khóa trong Bản đồ được sắp xếp trong khi các khóa được thêm vào đối tượng thì không. Do đó, khi lặp qua nó, một đối tượng Map sẽ trả về các khóa theo thứ tự chèn. (Lưu ý rằng trong ECMAScript 2015, các đối tượng đặc tả giữ nguyên thứ tự tạo cho các khóa chuỗi và Biểu tượng, do đó, việc truyền tải một đối tượng chỉ có các khóa chuỗi sẽ tạo ra các khóa theo thứ tự chèn)

Lưu ý, thứ tự các thuộc tính trong các đối tượng không được đảm bảo trước ES2015. Định nghĩa về một đối tượng từ ECMAScript Phiên bản thứ ba (pdf) :

4.3.3 Đối tượng

Một đối tượng là thành viên của loại Object. Nó là một tập hợp các thuộc tính không có thứ tự mà mỗi thuộc tính chứa một giá trị nguyên thủy, đối tượng hoặc hàm. Một hàm được lưu trữ trong một thuộc tính của một đối tượng được gọi là một phương thức.


Hành vi của các khóa số nguyên không nhất quán trên tất cả các trình duyệt. Một số trình duyệt cũ lặp lại các khóa số nguyên theo thứ tự chèn (với các phím chuỗi) và một số theo thứ tự tăng dần.
Dave Dopson

1
@DaveDopson - Các trình duyệt lỗi thời không tuân theo thông số kỹ thuật hiện tại vì chúng không được cập nhật.
TJ Crowder

199

CÓ (đối với các khóa không nguyên).

Hầu hết các trình duyệt lặp lại các thuộc tính đối tượng như:

  1. Các khóa số nguyên theo thứ tự tăng dần (và các chuỗi như "1" phân tích thành số nguyên)
  2. Các khóa chuỗi, theo thứ tự chèn (ES2015 đảm bảo điều này và tất cả các trình duyệt tuân thủ)
  3. Tên biểu tượng, theo thứ tự chèn (ES2015 đảm bảo điều này và tất cả các trình duyệt tuân thủ)

Một số trình duyệt cũ hơn kết hợp các loại # 1 và # 2, lặp lại tất cả các khóa theo thứ tự chèn. Nếu các khóa của bạn có thể phân tích thành số nguyên, tốt nhất không nên dựa vào bất kỳ thứ tự lặp cụ thể nào.

Thứ tự chèn ngôn ngữ hiện tại (kể từ ES2015) được giữ nguyên, ngoại trừ trong trường hợp các khóa phân tích thành số nguyên (ví dụ: "7" hoặc "99"), trong đó hành vi khác nhau giữa các trình duyệt. Ví dụ: Chrome / V8 không tôn trọng thứ tự chèn khi các phím được phân tích thành số.

Thông số ngôn ngữ cũ (trước ES2015) : Thứ tự lặp không được xác định về mặt kỹ thuật, nhưng tất cả các trình duyệt chính đều tuân thủ hành vi ES2015.

Lưu ý rằng hành vi ES2015 là một ví dụ điển hình về thông số ngôn ngữ được điều khiển bởi hành vi hiện có chứ không phải ngược lại. Để hiểu sâu hơn về tư duy tương thích ngược đó, hãy xem http://code.google.com.vn/p/v8/issues/detail?id=164 , một lỗi Chrome bao gồm chi tiết các quyết định thiết kế đằng sau hành vi đặt hàng lặp lại của Chrome . Theo một trong những ý kiến ​​(khá quan điểm) về báo cáo lỗi đó:

Các tiêu chuẩn luôn tuân theo các triển khai, đó là nơi XHR xuất phát và Google cũng làm điều tương tự bằng cách triển khai Gears và sau đó nắm lấy chức năng HTML5 tương đương. Cách khắc phục đúng là để ECMA chính thức kết hợp hành vi tiêu chuẩn thực tế vào phiên bản tiếp theo của thông số kỹ thuật.


2
@BenjaminGruenbaum - đó chính xác là quan điểm của tôi. Kể từ năm 2014, tất cả các nhà cung cấp lớn đã có một triển khai chung và do đó, các tiêu chuẩn cuối cùng sẽ tuân theo (tức là vào năm 2015).
Dave Dopson

2
Nhân tiện: React createFragmentAPI đã dựa vào điều này ...
mik01aj

7
@BenjaminGruenbaum Nhận xét của bạn là sai. Trong ES2015, đơn hàng chỉ được đảm bảo cho các phương thức được chọn . Xem câu trả lời của ftor dưới đây.
Piotr Dobrogost

82

Thứ tự thuộc tính trong các Đối tượng bình thường là một chủ đề phức tạp trong Javascript.

Mặc dù trong ES5 rõ ràng không có đơn hàng nào được chỉ định, ES2015 có một đơn đặt hàng trong một số trường hợp nhất định. Cho là đối tượng sau:

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

Điều này dẫn đến thứ tự sau (trong một số trường hợp nhất định):

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}
  1. các khóa giống như số nguyên theo thứ tự tăng dần
  2. các phím thông thường theo thứ tự chèn
  3. Ký hiệu theo thứ tự chèn

Do đó, có ba phân đoạn, có thể thay đổi thứ tự chèn (như đã xảy ra trong ví dụ). Và các khóa giống như số nguyên không dính vào thứ tự chèn nào cả.

Câu hỏi là, đối với phương thức nào, thứ tự này được đảm bảo trong thông số ES2015?

Các phương pháp sau đây đảm bảo thứ tự hiển thị:

  • Object.assign
  • Object.defineProperies
  • Object.getOwnPropertyNames
  • Object.getOwnPropertySymbols
  • Reflect.ownKeys

Các phương thức / vòng lặp sau đây đảm bảo không có thứ tự nào cả:

  • Object.keys
  • tại
  • JSON.parse
  • JSON.opesify

Kết luận: Ngay cả trong ES2015, bạn không nên dựa vào thứ tự thuộc tính của các đối tượng bình thường trong Javascript. Nó dễ bị lỗi. Sử dụng Mapthay thế.


Tôi gần như đã kiểm tra kết luận trong nút v8.11 và nó là chính xác.
merlin.ye

1
@BenjaminGruenbaum có suy nghĩ gì không?
Evolutionxbox

Thứ tự đảm bảo Object.entries, tuân theo quy tắc số nguyên
Mojimi

1
+1 cho "Ngay cả trong ES2015, bạn không nên dựa vào thứ tự thuộc tính của các đối tượng bình thường trong Javascript. Nó dễ bị lỗi. Thay vào đó, hãy sử dụng Bản đồ." Bạn không thể dựa vào thứ tự tài sản khi sự tuân thủ bị ảnh hưởng. Làm thế nào để bạn thậm chí kiểm tra điều này để so sánh ngược nếu bạn phải hỗ trợ các trình duyệt cũ hơn?
zero298

1
Có bất kỳ tài liệu chính thức về nó hoặc một tài liệu tham khảo?
Đánh bại

66

Tại thời điểm viết, hầu hết các trình duyệt đã trả về các thuộc tính theo cùng thứ tự khi chúng được chèn, nhưng rõ ràng nó không được đảm bảo về hành vi nên không nên dựa vào.

Đặc tả ECMAScript được sử dụng để nói:

Các cơ chế và thứ tự liệt kê các thuộc tính ... không được chỉ định.

Tuy nhiên, trong ES2015 và các khóa không nguyên sau này sẽ được trả về theo thứ tự chèn.


16
Chrome thực hiện một thứ tự khác với các trình duyệt khác. Xem code.google.com/p/v8/issues/detail?id=164
Tim Down

9
Opera 10.50 trở lên, cũng như IE9, khớp với thứ tự của Chrome. Firefox và Safari hiện là thiểu số (và cả hai cũng sử dụng các đơn đặt hàng khác nhau cho Đối tượng / Mảng).
gsnedder

1
@Veverke rõ ràng không có sự đảm bảo nào về đơn hàng vì vậy người ta phải luôn cho rằng đơn hàng thực sự ngẫu nhiên.
Alnitak

1
@Veverke Không, thứ tự là không có gì có thể dự đoán được. Việc triển khai phụ thuộc và có thể thay đổi bất cứ lúc nào và có thể thay đổi (ví dụ) mỗi khi trình duyệt của bạn tự cập nhật.
Alnitak

3
Câu trả lời này là sai trong ES2015.
Benjamin Gruenbaum

42

Toàn bộ câu trả lời này là trong bối cảnh tuân thủ thông số kỹ thuật, không phải bất kỳ động cơ nào làm tại một thời điểm cụ thể hoặc trong lịch sử.

Nói chung là không

Câu hỏi thực tế rất mơ hồ.

các thuộc tính sẽ theo thứ tự mà tôi đã thêm chúng

Trong bối cảnh nào?

Câu trả lời là: nó phụ thuộc vào một số yếu tố. Nói chung, không .

Thỉnh thoảng đúng

Đây là nơi bạn có thể tin tưởng vào thứ tự khóa thuộc tính cho đơn giản Objects:

  • Động cơ tuân thủ ES2015
  • Tài sản riêng
  • Object.getOwnPropertyNames(), Reflect.ownKeys(),Object.getOwnPropertySymbols(O)

Trong mọi trường hợp, các phương thức này bao gồm các khóa thuộc tính không thể đếm được và các khóa thứ tự như được chỉ định bởi [[OwnPropertyKeys]](xem bên dưới). Chúng khác nhau về loại giá trị chính mà chúng bao gồm ( Stringvà / hoặc Symbol). Trong bối cảnh này Stringbao gồm các giá trị số nguyên.

Object.getOwnPropertyNames(O)

Trả về Ocác Stringthuộc tính được khóa riêng ( tên thuộc tính ).

Reflect.ownKeys(O)

Trả về Ocác thuộc tính riêng Stringvà bị Symbolkhóa.

Object.getOwnPropertySymbols(O)

Trả về Ocác Symbolthuộc tính được khóa riêng của nó.

[[OwnPropertyKeys]]

Thứ tự về cơ bản là: giống như số nguyên Stringstheo thứ tự tăng dần, không giống số nguyênStrings theo thứ tự tạo, Biểu tượng theo thứ tự tạo. Tùy thuộc vào chức năng nào gọi điều này, một số loại này có thể không được bao gồm.

Ngôn ngữ cụ thể là các khóa được trả về theo thứ tự sau:

  1. ... mỗi khóa Pthuộc tính riêng của O[đối tượng được lặp lại] là một chỉ mục số nguyên, theo thứ tự chỉ số số tăng dần

  2. ... mỗi khóa Pthuộc tính riêng của chuỗi Ođó là một Chuỗi nhưng không phải là một chỉ số nguyên, theo thứ tự tạo thuộc tính

  3. ... mỗi khóa Pthuộc tính riêng của Onó là Biểu tượng, theo thứ tự tạo thuộc tính

Map

Nếu bạn quan tâm đến các bản đồ được đặt hàng, bạn nên cân nhắc sử dụng Maploại được giới thiệu trong ES2015 thay vì đơn giản Objects.


13

Trong các trình duyệt hiện đại, bạn có thể sử dụng Mapcấu trúc dữ liệu thay vì một đối tượng.

Nhà phát triển mozilla> Bản đồ

Một đối tượng Map có thể lặp lại các phần tử của nó theo thứ tự chèn ...


7

Trong ES2015, nó không, nhưng không như những gì bạn có thể nghĩ

Thứ tự các khóa trong một đối tượng không được đảm bảo cho đến ES2015. Nó được thực hiện xác định.

Tuy nhiên, trong ES2015 tại chỉ định. Giống như nhiều thứ trong JavaScript, điều này được thực hiện cho mục đích tương thích và thường phản ánh một tiêu chuẩn không chính thức hiện có trong số hầu hết các công cụ JS (với bạn là một người ngoại lệ).

Thứ tự được xác định trong thông số kỹ thuật, trong hoạt động trừu tượng OrdinaryOwnPropertyKeys , bao gồm tất cả các phương thức lặp trên các khóa riêng của đối tượng. Paraphrased, thứ tự như sau:

  1. Tất cả các chỉ số nguyên phím (những thứ như thế "1123", "55", vv) trong thứ tự tăng dần số.

  2. Tất cả các khóa chuỗi không phải là chỉ số nguyên, theo thứ tự tạo (cũ nhất trước).

  3. Tất cả các phím biểu tượng, theo thứ tự tạo (cũ nhất trước).

Thật ngớ ngẩn khi nói rằng thứ tự này không đáng tin cậy - nó đáng tin cậy, có lẽ nó không phải là thứ bạn muốn và các trình duyệt hiện đại thực hiện chính xác thứ tự này.

Một số trường hợp ngoại lệ bao gồm các phương pháp liệt kê các khóa được kế thừa, chẳng hạn như for .. invòng lặp. Các for .. invòng lặp không đảm bảo trật tự theo các đặc điểm kỹ thuật.


7

Kể từ ES2015, thứ tự thuộc tính được đảm bảo cho các phương thức nhất định lặp lại trên các thuộc tính. nhưng không phải người khác . Thật không may, các phương thức không được đảm bảo để có một đơn đặt hàng thường được sử dụng thường xuyên nhất:

  • Object.keys, Object.values,Object.entries
  • for..in vòng lặp
  • JSON.stringify

Nhưng, kể từ ES2020, thứ tự thuộc tính cho các phương thức không đáng tin cậy trước đây sẽ được đảm bảo bởi đặc tả được lặp lại theo cách xác định giống như các phương pháp khác, do đề xuất đã hoàn thành : cơ học for-in .

Cũng giống như với các phương thức có thứ tự lặp được bảo đảm (như Reflect.ownKeysObject.getOwnPropertyNames), các phương thức không được chỉ định trước đó cũng sẽ lặp theo thứ tự sau:

  • Các khóa mảng số, theo thứ tự số tăng dần
  • Tất cả các phím không phải Biểu tượng khác, theo thứ tự chèn
  • Phím biểu tượng, theo thứ tự chèn

Đây là điều mà hầu hết mọi việc thực hiện đã làm (và đã thực hiện trong nhiều năm), nhưng đề xuất mới đã biến nó thành chính thức.

Mặc dù thông số kỹ thuật hiện tại để lại..trong lệnh lặp " gần như không xác định , động cơ thực có xu hướng phù hợp hơn:"

Sự thiếu cụ thể trong ECMA-262 không phản ánh đúng thực tế. Trong các cuộc thảo luận trong nhiều năm trở lại đây, những người triển khai đã nhận thấy rằng có một số hạn chế đối với hành vi của for-in mà bất kỳ ai muốn chạy mã trên web cần phải tuân theo.

Bởi vì mọi triển khai đã lặp lại trên các thuộc tính có thể dự đoán được, nên nó có thể được đưa vào đặc tả mà không phá vỡ tính tương thích ngược.


Có một vài trường hợp kỳ lạ mà việc triển khai hiện không đồng ý và trong những trường hợp như vậy, thứ tự kết quả sẽ tiếp tục không được chỉ định. Đối với trật tự tài sản được đảm bảo :

Không phải đối tượng được lặp đi lặp lại hay bất cứ điều gì trong chuỗi nguyên mẫu của nó là proxy, mảng được gõ, đối tượng không gian tên mô-đun hoặc đối tượng kỳ lạ lưu trữ.

Cả đối tượng và bất cứ thứ gì trong chuỗi nguyên mẫu của nó đều có sự thay đổi nguyên mẫu trong quá trình lặp.

Cả đối tượng và bất cứ thứ gì trong chuỗi nguyên mẫu của nó đều có một thuộc tính bị xóa trong quá trình lặp.

Không có gì trong chuỗi nguyên mẫu của đối tượng có thuộc tính được thêm vào trong quá trình lặp.

Không có thuộc tính nào của đối tượng hoặc bất cứ thứ gì trong chuỗi nguyên mẫu của nó có sự thay đổi vô số trong quá trình lặp.

Không có tài sản không thể đếm được bóng tối một vô số.


5

Như những người khác đã nêu, bạn không có gì đảm bảo về thứ tự khi bạn lặp lại các thuộc tính của một đối tượng. Nếu bạn cần một danh sách theo thứ tự của nhiều trường, tôi đề nghị tạo một mảng các đối tượng.

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

Bằng cách này, bạn có thể sử dụng vòng lặp thông thường và có thứ tự chèn. Sau đó, bạn có thể sử dụng phương thức sắp xếp Array để sắp xếp điều này thành một mảng mới nếu cần.


3

Chỉ cần tìm ra điều này một cách khó khăn.

Sử dụng React với Redux, thùng chứa trạng thái mà tôi muốn truy cập để tạo ra trẻ em được làm mới mỗi khi cửa hàng được thay đổi (theo khái niệm bất biến của Redux).

Vì vậy, để lấy Object.keys(valueFromStore)tôi đã sử dụng Object.keys(valueFromStore).sort(), để bây giờ tôi ít nhất có một thứ tự chữ cái cho các phím.


-7

Từ tiêu chuẩn JSON :

Một đối tượng là một tập hợp không có thứ tự gồm các cặp tên / giá trị 0 hoặc nhiều hơn, trong đó tên là một chuỗi và giá trị là một chuỗi, số, boolean, null, đối tượng hoặc mảng.

(nhấn mạnh của tôi).

Vì vậy, không có bạn không thể đảm bảo trật tự.


7
điều này được chỉ định bởi tiêu chuẩn ECMAScript - không phải thông số JSON.
Alnitak

7
@Alnitak, @Iacqui: JSON chỉ lấy điều này từ đặc tả ECMAScript. Nó cũng được chỉ định cho JSON, nhưng điều này không thực sự liên quan đến câu hỏi.
Paŭlo Ebermann
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.