JavaScript cho số lượt truy cập so với


461

Bạn có nghĩ rằng có một sự khác biệt lớn trong ... trong và cho các vòng lặp không? Bạn thích sử dụng loại "cho" nào và tại sao?

Giả sử chúng ta có một mảng các mảng kết hợp:

var myArray = [{'key': 'value'}, {'key': 'value1'}];

Vì vậy, chúng ta có thể lặp lại:

for (var i = 0; i < myArray.length; i++)

Và:

for (var i in myArray)

Tôi không thấy một sự khác biệt lớn. Có bất kỳ vấn đề hiệu suất?


13
Lưu ý rằng chúng tôi cũng vậy, kể từ JS 1.6 , có : myArray.forEach(callback[, thisarg]).
Benji XVI

14
@Benji mảng.forEach thực sự có trong ES5.
mikemaccana

2
trong một vòng lặp for-in, bạn cần một điều kiện giống như thế này:if(myArray.hasOwnProperty(i)){true}
Eric Hodonsky

6
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); Bạn có thể sử dụng khá nhiều ở mọi nơi trừ IE8- và đó là cú pháp tao nhã nhất
Jon z

5
Cũng có for...oftuyên bố trong ECMAScript 6 , ví dụ:for (let i of myArray) console.log(i);
Vitalii Fedorenko

Câu trả lời:


548

Sự lựa chọn nên dựa trên thành ngữ nào được hiểu rõ nhất.

Một mảng được lặp lại bằng cách sử dụng:

for (var i = 0; i < a.length; i++)
   //do stuff with a[i]

Một đối tượng đang được sử dụng như một mảng kết hợp được lặp lại bằng cách sử dụng:

for (var key in o)
  //do stuff with o[key]

Trừ khi bạn có lý do tan vỡ trái đất, hãy bám vào mô hình sử dụng đã được thiết lập.


38
Cần phải đề cập rằng đó là một cách thực hành hay để sử dụng cho ... trong việc lọc if. Có một phương thức hữu ích của Object "obj.hasOwnProperty (thành viên)" để kiểm tra xem một thành viên được trả về bởi iterator có thực sự là thành viên của đối tượng không. Xem: javascript.crockford.com/code.html
Damir Zekić

57
Như đã nhận xét tại (các) câu trả lời khác, "for ... in" không hoạt động chính xác cho Mảng, vì nó sẽ lặp lại trên tất cả các thuộc tính và phương thức của Array. Vì vậy, bạn chỉ nên sử dụng "for ... in" để lặp lại các thuộc tính đối tượng. Mặt khác, hãy bám vào "for (i = 0; i <Something; i ++)"
Denilson Sá Maia

Vì lý do hiệu suất, IMO tốt hơn là đánh giá độ dài của mảng trước for, chứ không phải đánh giá a.length mỗi lần trong vòng lặp.
UpTheCux

9
@UpTheCalet: Điều đó hoàn toàn đúng khi mảng thực tế là thứ được HTMLDOM trả về, tuy nhiên, tôi tự hỏi một mảng javascript tiêu chuẩn sẽ cần lớn đến mức nào trước khi bạn có thể thấy sự khác biệt đáng kể? Cá nhân tôi sẽ giữ mã đơn giản nhất có thể cho đến khi nó được chứng minh là cần thiết để làm một cái gì đó khác biệt.
AnthonyWJones

2
@Pichan Tôi nghĩ bạn có nghĩa là i < l, không i < a, trong điều kiện vòng lặp for của bạn.
Max Nanasy

161

Douglas Crockford khuyến nghị trong JavaScript: Các bộ phận tốt (trang 24) để tránh sử dụng for intuyên bố.

Nếu bạn sử dụng for inđể lặp qua tên thuộc tính trong một đối tượng, kết quả sẽ không được sắp xếp. Tệ hơn: Bạn có thể nhận được kết quả bất ngờ; nó bao gồm các thành viên được kế thừa từ chuỗi nguyên mẫu và tên của các phương thức.

Tất cả mọi thứ trừ các thuộc tính có thể được lọc ra .hasOwnProperty. Mẫu mã này thực hiện những gì bạn có thể muốn ban đầu:

for (var name in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, name)) {
        // DO STUFF
    }
}

70
cho ... trong là hoàn toàn thích hợp để lặp qua các thuộc tính đối tượng. Nó không thích hợp để lặp qua các phần tử mảng. Nếu bạn không thể hiểu được sự khác biệt giữa các kịch bản này, thì có, bạn nên tránh cho ... vào; nếu không, đi hạt.
Shog9

8
Muốn nhấn mạnh sự thật rằng nó KHÔNG ĐẶT HÀNG! Đây có thể là một vấn đề lớn và sẽ là một lỗi khó bắt.
Jason

4
+1 cho "nó bao gồm các thành viên được kế thừa từ chuỗi nguyên mẫu và tên của các phương thức." Chẳng hạn, bạn sẽ rất vui nếu ai đó tình cờ sử dụng mã của bạn với Prototype được tải (ngay cả khi mã của bạn không thực sự sử dụng nó), chẳng hạn.
ijw

13
Xin đừng quên khai báo các namebiến: for(var name in object)..., nếu không, nếu mã số đó là bên trong một hàm ví dụ, namecuối biến lên trở thành một tài sản của đối tượng toàn cầu (chuyển nhượng cho một định danh chưa được khai báo nào đó), còn trong ECMAScript mới 5 Chế độ nghiêm ngặt, mã đó sẽ ném a ReferenceError.
CMS

6
@Nosredna: Có một vấn đề về thứ tự lặp cho Chrome, được gửi bởi không ai khác ngoài John Resig, được đánh dấu là WontFix. code.google.com/p/chromium/issues/detail?id=883 . Ngay cả trước chrome, thứ tự lặp không giống nhau trên các trình duyệt nếu bạn xóa và sau đó thêm một thuộc tính. Ngoài ra IE 9 hoạt động rất giống chrome (được cho là để cải thiện tốc độ). Vì vậy, ... Hãy ngừng lan truyền thông tin không chính xác, bạn sẽ rất ngây thơ để giữ tùy thuộc vào nó.
Juan Mendes

62

FYI - Người dùng jQuery


each(callback)Phương thức của jQuery sử dụng for( ; ; )vòng lặp theo mặc định và sẽ for( in ) chỉ sử dụng nếu độ dài là undefined.

Do đó, tôi sẽ nói an toàn khi đảm nhận đúng thứ tự khi sử dụng chức năng này.

Ví dụ :

$(['a','b','c']).each(function() {
    alert(this);
});
//Outputs "a" then "b" then "c"

Nhược điểm của việc sử dụng này là nếu bạn đang thực hiện một số logic không phải UI, các chức năng của bạn sẽ kém khả dụng hơn đối với các khung công tác khác. Các each()chức năng có lẽ là tốt nhất dành cho sử dụng với bộ chọn jQuery và for( ; ; )có thể được khuyến khích khác.



4
Luôn có documentcloud.github.com/underscore có _.each và rất nhiều chức năng hữu ích khác
w00t

1
điều đó cũng có nghĩa là nếu tôi có thuộc tính độ dài trong đối tượng của mình $ .each sẽ thất bại? ví dụ: x = {a: "1", b: "2", chiều dài: 3}.
Onur Topal

29

có sự khác biệt về hiệu suất tùy thuộc vào loại vòng lặp bạn sử dụng và trên trình duyệt nào.

Ví dụ:

for (var i = myArray.length-1; i >= 0; i--)

nhanh hơn gần gấp đôi trên một số trình duyệt so với:

for (var i = 0; i < myArray.length; i++)

Tuy nhiên, trừ khi mảng của bạn rất LỚN hoặc bạn lặp chúng liên tục, tất cả đều đủ nhanh. Tôi thực sự nghi ngờ rằng việc lặp mảng là một nút cổ chai trong dự án của bạn (hoặc cho bất kỳ dự án nào khác cho vấn đề đó)


5
Việc lưu trữ "myArray.length" vào một biến trước khi lặp sẽ làm cho sự khác biệt hiệu suất biến mất? Tôi đoán là "có".
Tomalak

3
Số 'myArray.length' là một thuộc tính, không phải là một phương thức trên một đối tượng - không có phép tính nào được thực hiện để xác định giá trị của nó. Lưu trữ giá trị của nó trong một biến sẽ không làm gì cả.
jason

3
Có, có. Thuộc tính không phải là biến; họ có mã get / set.
ste

12
Tôi có xu hướng sử dụngfor(var i = myArray.length; i--;)
Kevin

2
Mã cho rõ ràng và dễ đọc. Không cho những gì xảy ra để chạy nhanh hơn một chút trong một số trình duyệt khi sử dụng các mảng lớn vô lý tại thời điểm hiện tại. Tối ưu hóa có thể thay đổi, nhưng khả năng đọc (hoặc thiếu mã) của mã của bạn sẽ không. Viết mã mà người khác có thể dễ dàng theo dõi và để cho trình tối ưu hóa bắt kịp trong thời gian đáo hạn của riêng họ.
aroth

26

Lưu ý rằng phương thức Array.forEach riêng hiện được hỗ trợ rộng rãi .


2
Nó làm gì? Liệu nó có vấn đề được đề cập trong các bài viết khác (lặp qua các thuộc tính thay vì các phần tử của mảng)? Ngoài ra, vì IE8 không hỗ trợ nó, nên hơi khó để nói rằng nó được hỗ trợ rộng rãi.
Rauni Lillemets

2
nhiều như người dùng * nix coi thường nó, IE8 hầu hết là tất cả người dùng windows7. đó là một phần lớn của thị trường trình duyệt.
sbartell

2
@Rauni - Tôi đưa ra quan điểm của bạn, nhưng đối với các thiết bị máy tính để bàn, chia sẻ trình duyệt IE ít hơn 40%, theo en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table và theo chợhare.hitslink.com / các trang web khác, ít nhất 8% trình duyệt là IE 9. Nói cách khác, Array.forEach được hỗ trợ bởi khoảng 70% trình duyệt máy tính để bàn, vì vậy tôi không nghĩ rằng 'được hỗ trợ rộng rãi' là không hợp lý. Tôi chưa kiểm tra, nhưng hỗ trợ di động (trên trình duyệt WebKit và Opera) có thể còn cao hơn. Rõ ràng, có những thay đổi đáng kể về mặt địa lý.
Sam Dutton

1
Cảm ơn các cập nhật. Tôi đồng ý rằng có thể nói rằng nó "được hỗ trợ rộng rãi". Vấn đề duy nhất là nếu người dùng sử dụng phương thức JS này, anh ấy / cô ấy vẫn phải viết một phương thức sao lưu cho trường hợp nếu nó không được hỗ trợ ..
Rauni Lillemets

1
@Rauni - bạn có thể sử dụng es5-shim và es6-shim để tự động cung cấp các phương thức sao lưu. github.com/es-shims/es5-shim
Michiel van der Blonk

24

Câu trả lời được cập nhật cho phiên bản hiện tại năm 2012 của tất cả các trình duyệt chính - Chrome, Firefox, IE9, Safari và Opera hỗ trợ mảng gốc.forEach của ES5.

Trừ khi bạn có một số lý do để hỗ trợ IE8 nguyên bản (lưu ý khung ES5-shim hoặc Chrome có thể được cung cấp cho những người dùng này, điều này sẽ cung cấp một môi trường JS thích hợp), đơn giản hơn là sử dụng cú pháp đúng của ngôn ngữ:

myArray.forEach(function(item, index) {
    console.log(item, index);
});

Tài liệu đầy đủ cho mảng.forEach () có tại MDN.


1
Bạn thực sự nên ghi lại các tham số của hàm gọi lại: Thứ nhất là giá trị phần tử, thứ 2 chỉ số phần tử, thứ 3 mảng được dịch chuyển
thu hút

Tôi nghe thấy những gì bạn đang nói, nhưng trong trường hợp này, việc quá đơn giản hóa nó che khuất toàn bộ khả năng. Có cả chỉ số và giá trị có nghĩa là nó có thể đóng vai trò thay thế cho cả ... trong và cho mỗi ... trong một phần thưởng với phần thưởng mà bạn không cần phải nhớ lặp lại qua các khóa hoặc giá trị.
thu hút

1
@Cory: ES5 forEach có thể được thêm vào các trình duyệt ES3 kế thừa khá dễ dàng. Mã ít hơn là mã tốt hơn.
mikemaccana

2
@nailer Điều này có thể được sử dụng trên các mảng và các đối tượng thay thế cho nhau không?
hitautodesturation

1
@hitautodesturation Đó là một phần của nguyên mẫu của Array, không phải của Object. Nói chung trong cộng đồng ngay bây giờ các đối tượng không phải là Array vẫn được lặp lại với 'for (var key in object) {}'.
mikemaccana

14

Hai cái này không giống nhau khi mảng thưa thớt.

var array = [0, 1, 2, , , 5];

for (var k in array) {
  // Not guaranteed by the language spec to iterate in order.
  alert(k);  // Outputs 0, 1, 2, 5.
  // Behavior when loop body adds to the array is unclear.
}

for (var i = 0; i < array.length; ++i) {
  // Iterates in order.
  // i is a number, not a string.
  alert(i);  // Outputs 0, 1, 2, 3, 4, 5
  // Behavior when loop body modifies array is clearer.
}

14

Sử dụng forEach để bỏ qua chuỗi nguyên mẫu

Chỉ cần một phụ lục nhanh cho câu trả lời của @ nailer ở trên , sử dụng forEach với Object.keys có nghĩa là bạn có thể tránh lặp lại chuỗi nguyên mẫu mà không phải sử dụng hasOwnProperty.

var Base = function () {
    this.coming = "hey";
};

var Sub = function () {
    this.leaving = "bye";
};

Sub.prototype = new Base();
var tst = new Sub();

for (var i in tst) {
    console.log(tst.hasOwnProperty(i) + i + tst[i]);
}

Object.keys(tst).forEach(function (val) {
    console.log(val + tst[val]);
});

2
chết tiệt, đó là lén lút. Thật đáng để đọc 50 bài đăng khác để có được điều này. obj = {"hồng": "vịt", đỏ: "ngỗng"}; Object.keys (obj) === ["hồng", "đỏ"]
Orwellophile

14

Tôi thứ hai ý kiến ​​rằng bạn nên chọn phương pháp lặp theo nhu cầu của bạn. Tôi sẽ đề nghị bạn thực sự không bao giờ lặp qua bản địa Arrayvới for incấu trúc. Đó là cách chậm hơn , như Chase Seibert đã chỉ ra vào lúc trước, không tương thích với khung Prototype.

Có một điểm chuẩn tuyệt vời về các kiểu lặp khác nhau mà bạn hoàn toàn nên xem nếu bạn làm việc với JavaScript . Đừng tối ưu hóa sớm, nhưng bạn nên giữ những thứ đó ở đâu đó sau gáy.

Tôi sẽ sử dụng for inđể có được tất cả các thuộc tính của một đối tượng, điều này đặc biệt hữu ích khi gỡ lỗi các tập lệnh của bạn. Ví dụ: tôi thích có dòng này tiện dụng khi tôi khám phá đối tượng lạ:

l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.log(l);

Nó chuyển nội dung của toàn bộ đối tượng (cùng với các thân phương thức) vào nhật ký Firebird của tôi. Rất tiện dụng.


Liên kết bây giờ bị hỏng. Chắc chắn muốn xem điểm chuẩn, nếu ai đó có một liên kết khác.
Billbad

Nó không bị hỏng nữa.
Olli

Vòng lặp Foreach phá vỡ trong nguyên mẫu? Vì hiện nay nó thường được hỗ trợ, đó là điều mà nguyên mẫu nên được giải quyết.
mvrak

7

đây là một cái gì đó tôi đã làm

function foreach(o, f) {
 for(var i = 0; i < o.length; i++) { // simple for loop
  f(o[i], i); // execute a function and make the obj, objIndex available
 }
}

đây là cách bạn sẽ sử dụng nó, nó
sẽ hoạt động trên các mảng và đối tượng (chẳng hạn như danh sách các phần tử HTML)

foreach(o, function(obj, i) { // for each obj in o
  alert(obj); // obj
  alert(i); // obj index
  /*
    say if you were dealing with an html element may be you have a collection of divs
  */
  if(typeof obj == 'object') { 
   obj.style.marginLeft = '20px';
  }
});

Tôi chỉ thực hiện điều này vì vậy tôi mở để đề xuất :)


Công cụ tuyệt vời - khá đơn giản!
Chris

6

Tôi sẽ sử dụng các phương pháp khác nhau dựa trên cách tôi muốn tham khảo các mục.

Sử dụng foreach nếu bạn chỉ muốn các mục hiện tại.

Sử dụng nếu bạn cần một người lập chỉ mục để so sánh tương đối. (Tức là làm thế nào để so sánh với mục trước / tiếp theo?)

Tôi chưa bao giờ nhận thấy một sự khác biệt hiệu suất. Tôi sẽ đợi cho đến khi có một vấn đề hiệu suất trước khi lo lắng về nó.


Xem câu trả lời của Bnos bên dưới - vì ... không làm những gì bạn mong đợi ở đây và nếu bạn sử dụng nó, bạn có thể có tất cả các loại niềm vui. Đối với hồ sơ, Prototype thực hiện mọi thứ đúng cách.
marcus.greasly

4

Với for (var i in myArray), bạn cũng có thể lặp qua các đối tượng, tôi sẽ chứa tên khóa và bạn có thể truy cập thuộc tính thông qua myArray [i] . Ngoài ra, bất kỳ phương thức nào bạn đã thêm vào đối tượng cũng sẽ được bao gồm trong vòng lặp, tức là, nếu bạn sử dụng bất kỳ khung công tác bên ngoài nào như jQuery hoặc nguyên mẫu hoặc nếu bạn thêm phương thức vào các nguyên mẫu đối tượng trực tiếp, tại một điểm tôi sẽ trỏ đến những phương pháp đó.


4

Coi chừng!

Ví dụ: nếu bạn có một số thẻ script và bạn đang tìm kiếm thông tin trong các thuộc tính thẻ, bạn phải sử dụng thuộc tính .length với một vòng lặp for vì đó không phải là một mảng đơn giản mà là một đối tượng HTMLCollection.

https://developer.mozilla.org/en/DOM/HTMLCollection

Nếu bạn sử dụng câu lệnh foreach cho (var i trong yourList), nó sẽ trả về các biểu thức và phương thức của HTMLCollection trong hầu hết các trình duyệt!

var scriptTags = document.getElementsByTagName("script");

for(var i = 0; i < scriptTags.length; i++)
alert(i); // Will print all your elements index (you can get src attribute value using scriptTags[i].attributes[0].value)

for(var i in scriptTags)
alert(i); // Will print "length", "item" and "namedItem" in addition to your elements!

Ngay cả khi getElementsByTagName phải trả về NodeList, hầu hết các trình duyệt đều trả về HTMLCollection: https://developer.mozilla.org/en/DOM/document.getElementsByTagName


3

Đối với các vòng lặp trên Mảng không tương thích với Prototype. Nếu bạn nghĩ rằng bạn có thể cần phải sử dụng thư viện đó trong tương lai, thì sẽ rất hợp lý nếu bạn sử dụng các vòng lặp.

http://www.prototypejs.org/api/array


Quên "bạn có thể cần sử dụng thư viện đó". Thay vào đó hãy nghĩ rằng "JS của bạn có thể được bao gồm với bất kỳ thứ gì khác sử dụng thư viện đó", bởi vì vấn đề vẫn còn.
ijw

3

Tôi đã thấy các vấn đề với "cho mỗi" khi sử dụng các đối tượng và nguyên mẫu và mảng

sự hiểu biết của tôi là cho mỗi là cho các thuộc tính của các đối tượng và KHÔNG phải mảng


3

Nếu bạn thực sự muốn tăng tốc mã của mình, thì sao?

for( var i=0,j=null; j=array[i++]; foo(j) );

đó là loại có logic trong khi câu lệnh for và nó ít dư thừa hơn. Ngoài ra firefox có Array.forEach và Array.filter


2
Tại sao điều này sẽ tăng tốc mã của bạn? Tôi không thể hiểu tại sao phát biểu lại như thế này sẽ tăng tốc nó.
Rupi

3

Một mã ngắn hơn và tốt nhất theo jsperf là

keys  = Object.keys(obj);
for (var i = keys.length; i--;){
   value = obj[keys[i]];// or other action
}

1

Sử dụng vòng lặp Array (). ForEach để tận dụng sự song song


4
JavaScript trong trình duyệt là vòng lặp sự kiện đồng thời, do đó Array.prototype.forEachsẽ không thực hiện nhiều cuộc gọi đến cuộc gọi lại song song.
Mike Samuel

1

cho (;;) là cho Mảng : [20,55,33]

for..in dành cho Đối tượng : {x: 20, y: 55: z: 33}


0

Hãy cẩn thận!!! Tôi đang sử dụng Chrome 22.0 trong Mac OS và tôi gặp vấn đề với từng cú pháp.

Tôi không biết đây có phải là sự cố trình duyệt, sự cố javascript hoặc một số lỗi trong mã không, nhưng nó RẤT lạ. Bên ngoài đối tượng nó hoạt động hoàn hảo.

var MyTest = {
    a:string = "a",
    b:string = "b"
};

myfunction = function(dicts) {
    for (var dict in dicts) {
        alert(dict);
        alert(typeof dict); // print 'string' (incorrect)
    }

    for (var i = 0; i < dicts.length; i++) {
        alert(dicts[i]);
        alert(typeof dicts[i]); // print 'object' (correct, it must be {abc: "xyz"})
    }
};

MyObj = function() {
    this.aaa = function() {
        myfunction([MyTest]);
    };
};
new MyObj().aaa(); // This does not work

myfunction([MyTest]); // This works

0

Có một sự khác biệt quan trọng giữa cả hai. Lặp đi lặp lại trên các thuộc tính của một đối tượng, vì vậy khi trường hợp là một mảng, nó sẽ không chỉ lặp lại các phần tử của nó mà còn qua chức năng "loại bỏ" mà nó có.

for (var i = 0; i < myArray.length; i++) { 
    console.log(i) 
}

//Output
0
1

for (var i in myArray) { 
    console.log(i) 
} 

// Output
0 
1 
remove

Bạn có thể sử dụng for-in với một if(myArray.hasOwnProperty(i)). Tuy nhiên, khi lặp qua các mảng, tôi luôn muốn tránh điều này và chỉ sử dụng câu lệnh for (;;).


0

Mặc dù cả hai đều rất giống nhau nhưng có một sự khác biệt nhỏ:

var array = ["a", "b", "c"];
array["abc"] = 123;
console.log("Standard for loop:");
for (var index = 0; index < array.length; index++)
{
  console.log(" array[" + index + "] = " + array[index]); //Standard for loop
}

trong trường hợp này, đầu ra là:

TIÊU CHUẨN CHO CUỘC ĐỜI:

ARRAY [0] = A

ARRAY [1] = B

ARRAY [2] = C

console.log("For-in loop:");
for (var key in array)
{
  console.log(" array[" + key + "] = " + array[key]); //For-in loop output
}

trong khi trong trường hợp này, đầu ra là:

CUỘC ĐỜI

ARRAY [1] = B

ARRAY [2] = C

ARRAY [10] = D

ARRAY [ABC] = 123


0

Câu lệnh for in cho phép lặp qua tên của tất cả các thuộc tính của một đối tượng. Thật không may, nó cũng lặp qua tất cả các thành viên được kế thừa thông qua chuỗi nguyên mẫu. Điều này có tác dụng phụ xấu trong việc phục vụ các chức năng của phương thức khi sự quan tâm đến các thành viên dữ liệu.

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.