Các yếu tố theo thứ tự trong một vòng lặp cho (


204

Có phải vòng lặp "cho bit trong" trong vòng lặp Javascript thông qua các hàm băm / phần tử theo thứ tự chúng được khai báo không? Có một trình duyệt không làm theo thứ tự?
Đối tượng tôi muốn sử dụng sẽ được khai báo một lần và sẽ không bao giờ được sửa đổi.

Giả sử tôi có:

var myObject = { A: "Hello", B: "World" };

Và tôi tiếp tục sử dụng chúng trong:

for (var item in myObject) alert(item + " : " + myObject[item]);

Tôi có thể mong đợi 'A: "Xin chào"' luôn xuất hiện trước 'B: "Thế giới"' trong hầu hết các trình duyệt phong nha không?


61
Vì họ sẽ chỉ thử nghiệm một tập hợp con của các trình duyệt và biến thể tiềm năng. Không đề cập đến bất kỳ trình duyệt trong tương lai. Thật sai lầm khi cho rằng một thử nghiệm không thất bại cung cấp bất kỳ loại bằng chứng cụ thể nào.
James Hughes

6
Tôi nghi ngờ khả năng javascript hạn chế của riêng tôi sẽ tốt hơn đám đông SO. Bên cạnh đó ai biết trình duyệt lạ nào ẩn giấu ngoài kia? Và bạn có thể thấy trong câu trả lời rằng GChrom có ​​một lỗi không rõ ràng trong trường hợp ví dụ đơn giản của tôi.
chakrit


Câu trả lời:


214

Trích dẫn John Resig :

Hiện tại tất cả các trình duyệt chính lặp lại các thuộc tính của một đối tượng theo thứ tự mà chúng được xác định. Chrome cũng làm điều này, ngoại trừ một vài trường hợp. [...] Hành vi này rõ ràng không được xác định bởi đặc tả ECMAScript. Trong ECMA-262, phần 12.6.4:

Các cơ chế liệt kê các thuộc tính ... phụ thuộc vào việc thực hiện.

Tuy nhiên, đặc điểm kỹ thuật khá khác với thực hiện. Tất cả các triển khai hiện đại của ECMAScript lặp đi lặp lại thông qua các thuộc tính đối tượng theo thứ tự mà chúng được xác định. Do đó, nhóm Chrome đã coi đây là một lỗi và sẽ khắc phục nó.

Tất cả các trình duyệt đều tôn trọng thứ tự định nghĩa ngoại trừ Chrome và Opera dành cho mọi tên thuộc tính không phải là số. Trong hai trình duyệt này, các thuộc tính được kéo theo thứ tự trước thuộc tính không số đầu tiên (điều này có liên quan đến cách chúng thực hiện các mảng). Thứ tự là giống nhau cho Object.keyslà tốt.

Ví dụ này sẽ làm rõ những gì xảy ra:

var obj = {
  "first":"first",
  "2":"2",
  "34":"34",
  "1":"1",
  "second":"second"
};
for (var i in obj) { console.log(i); };
// Order listed:
// "1"
// "2"
// "34"
// "first"
// "second"

Các kỹ thuật của điều này ít quan trọng hơn thực tế là điều này có thể thay đổi bất cứ lúc nào. Đừng dựa vào những thứ theo cách này.

Tóm lại: Sử dụng một mảng nếu thứ tự là quan trọng đối với bạn.


3
Không hẳn vậy. Chrome không triển khai thứ tự giống như các trình duyệt khác: code.google.com/p/v8/issues/detail?id=164
Tim Down

19
"Sử dụng một mảng nếu thứ tự là quan trọng đối với bạn": Còn khi bạn sử dụng JSON thì sao?
HM2K

11
@ HM2K, Điều tương tự, spec nói "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." JSON không phải là JavaScript: Một máy chủ không cần (và có thể sẽ không) tôn trọng thứ tự bạn trao nó.
Borgar

7
Firefox kể từ phiên bản 21 của nó dường như không còn tôn trọng thứ tự chèn nữa.
Doc Davluz

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

54

Va chạm này một năm sau ...

Đó là năm 2012 và các trình duyệt chính vẫn khác nhau:

function lineate(obj){
    var arr = [], i;
    for (i in obj) arr.push([i,obj[i]].join(':'));
    console.log(arr);
}
var obj = { a:1, b:2, c:3, "123":'xyz' };
/* log1 */  lineate(obj);
obj.a = 4;
/* log2 */  lineate(obj);
delete obj.a;
obj.a = 4;
/* log3 */  lineate(obj);

ý chính hoặc kiểm tra trong trình duyệt hiện tại

Safari 5, Firefox 14

["a:1", "b:2", "c:3", "123:xyz"]
["a:4", "b:2", "c:3", "123:xyz"]
["b:2", "c:3", "123:xyz", "a:4"]

Chrome 21, Opera 12, Nút 0,6, Firefox 27

["123:xyz", "a:1", "b:2", "c:3"]
["123:xyz", "a:4", "b:2", "c:3"]
["123:xyz", "b:2", "c:3", "a:4"]

IE9

[123:xyz,a:1,b:2,c:3] 
[123:xyz,a:4,b:2,c:3] 
[123:xyz,a:4,b:2,c:3] 

16
vấn đề ở đây là gì? Chính xác như thông số kỹ thuật nói, đó là một danh sách các cặp khóa / val không có thứ tự.
nickf

5
nickf: việc triển khai đúng ở chỗ họ làm những gì spec nói. Tôi nghĩ mọi người đều đồng ý rằng thông số javascript là .. tốt, tôi không muốn sử dụng từ "sai", còn "cực kỳ ngu ngốc và khó chịu" thì sao? : P
đóng hộp

10
@boxed: Hãy nghĩ về các đối tượng như một bản đồ băm (bảng / bất cứ thứ gì). Trong hầu hết các ngôn ngữ (Java, Python, v.v.) các loại cấu trúc dữ liệu này không được sắp xếp. Vì vậy, không có gì đáng ngạc nhiên khi đây cũng là trường hợp tương tự trong JavaScript và chắc chắn nó không làm cho đặc tả sai hoặc ngu ngốc.
Felix Kling

9
Tôi thực sự không thấy điểm của câu trả lời này (xin lỗi). Thứ tự các thuộc tính được lặp đi lặp lại là một chi tiết triển khai và các trình duyệt sử dụng các công cụ JavaScript khác nhau, do đó, thứ tự được dự kiến ​​sẽ khác nhau. Điều này sẽ không thay đổi.
Felix Kling

2
Trong trạng thái triển khai nghệ thuật, bạn sẽ nhận được các thuộc tính số nguyên theo thứ tự giá trị số do được hỗ trợ bởi một mảng thực và các thuộc tính được đặt tên sẽ được nhận theo thứ tự chèn do công thức lớp / hình dạng ẩn. Điều có thể thay đổi là liệu họ liệt kê các thuộc tính số nguyên trước hay thuộc tính được đặt tên trước. Việc bổ sung deleterất thú vị vì ít nhất là trong V8, nó ngay lập tức khiến đối tượng được sao lưu bằng bảng băm. Tuy nhiên, bảng băm trong các cửa hàng V8 theo thứ tự chèn. Kết quả thú vị nhất ở đây là IE, tôi tự hỏi họ đã làm điều xấu xí nào để loại bỏ điều đó ...
Esailija

27

Từ Đặc tả ngôn ngữ ECMAScript , phần 12.6.4 (trên for .. invòng lặp):

Các cơ chế liệt kê các thuộc tính là phụ thuộc thực hiện. Thứ tự liệt kê được xác định bởi đối tượng.

Và phần 4.3.3 (định nghĩa của "Đối tượng"):

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.

Tôi đoán điều đó có nghĩa là bạn không thể dựa vào các thuộc tính được liệt kê theo thứ tự nhất quán trong các triển khai JavaScript. (Dù sao nó cũng sẽ là phong cách xấu khi dựa vào các chi tiết cụ thể của ngôn ngữ.)

Nếu bạn muốn thứ tự của bạn được xác định, bạn sẽ cần phải thực hiện một cái gì đó xác định nó, giống như một mảng các khóa mà bạn sắp xếp trước khi truy cập vào đối tượng với nó.


10

Các phần tử của một đối tượng cho / trong liệt kê là các thuộc tính không có cờ DontEnum được đặt. Tiêu chuẩn ECMAScript, hay còn gọi là Javascript, nói rõ ràng rằng "Đối tượng là một tập hợp các thuộc tính không có thứ tự" (xem http://www.mozilla.org/js/lingu/E262-3.pdf phần 8.6).

Sẽ không tuân thủ các tiêu chuẩn (nghĩa là an toàn) khi cho rằng tất cả các triển khai Javascript sẽ liệt kê theo thứ tự khai báo.


2
đó là lý do tại sao anh ấy đặt câu hỏi, thay vì chỉ giả sử: p
Jamie Pate

4

Thứ tự lặp cũng bị nhầm lẫn với việc xóa các thuộc tính, nhưng trong trường hợp này chỉ với IE.

var obj = {};
obj.a = 'a';
obj.b = 'b';
obj.c = 'c';

// IE allows the value to be deleted...
delete obj.b;

// ...but remembers the old position if it is added back later
obj.b = 'bb';
for (var p in obj) {
    alert(obj[p]); // in IE, will be a, bb, then c;
                   // not a, c, then bb as for FF/Chrome/Opera/Safari
}

Mong muốn thay đổi thông số kỹ thuật để sửa thứ tự lặp có vẻ là một mong muốn khá phổ biến của các nhà phát triển nếu cuộc thảo luận tại http://code.google.com.vn/p/v8/issues/detail?id=164 là bất kỳ dấu hiệu nào.


3

trong IE6, thứ tự không được đảm bảo.


2

Đơn hàng không thể tin cậy được. Cả Opera và Chrome đều trả về danh sách các thuộc tính không có thứ tự.

<script type="text/javascript">
var username = {"14719":"A","648":"B","15185":"C"};

for (var i in username) {
  window.alert(i + ' => ' + username[i]);
}
</script>

Đoạn mã trên hiển thị B, A, C trong Opera và C, A, B trong Chrome.


1

Điều này không trả lời câu hỏi mỗi lần, nhưng đưa ra một giải pháp cho vấn đề cơ bản.

Giả sử rằng bạn không thể dựa vào thứ tự để bảo tồn, tại sao không sử dụng một mảng các đối tượng có khóa và giá trị làm thuộc tính?

var myArray = [
    {
        'key'   : 'key1'
        'value' : 0
    },
    {
        'key'   : 'key2',
        'value' : 1
    } // ...
];

Bây giờ, tùy thuộc vào bạn để đảm bảo rằng các khóa là duy nhất (giả sử rằng điều này cũng quan trọng đối với bạn. Đồng thời, thay đổi địa chỉ trực tiếp và cho (... trong ...) bây giờ trả về các chỉ mục dưới dạng 'khóa'.

> console.log(myArray[0].key);
key1

> for (let index in myArray) {console.log(myArray[index].value);}
0
1
Xem Bút để biết địa chỉ (... trong ...) theo thứ tự của JDQ ( @JDQ ) trên CodePen .

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.