Tại sao việc sử dụng cho các ứng dụng trên mạng với các công cụ lặp lại trong mảng với một ý tưởng tồi?


1823

Tôi đã được khuyên không sử dụng for...invới các mảng trong JavaScript. Tại sao không?


45
Tôi thấy câu hỏi gần đây, nơi ai đó nói điều đó với bạn, nhưng chúng chỉ có ý nghĩa cho Mảng. Nó được coi là thực hành xấu khi lặp qua các mảng nhưng không nhất thiết phải lặp qua các thành viên của một đối tượng.
mmurch

19
Rất nhiều câu trả lời với các vòng lặp "for", chẳng hạn như 'for (var i = 0; i <hColl.length; i ++) {}' so với 'var i = hColl.length; while (i--) {} 'mà, khi có thể sử dụng dạng sau thì nó nhanh hơn đáng kể. Tôi biết điều này là tiếp tuyến nhưng nghĩ rằng tôi sẽ thêm bit này.
Mark Schultheiss

2
@MarkSchultheiss nhưng đó là phép lặp ngược. Có phiên bản lặp lại chuyển tiếp nào nhanh hơn không?
ma11hew28

5
@Wynand sử dụng var i = hCol1.length; for (i;i;i--;) {}bộ đệm i vì nó sẽ tạo ra sự khác biệt và đơn giản hóa việc kiểm tra. - trình duyệt càng cũ, sự khác biệt giữa forwhileLUÔN LUÔN lưu trữ bộ đếm "i" - và tất nhiên tiêu cực không phải lúc nào cũng phù hợp với tình huống và tiêu cực trong khi obfuscate một số mã cho một số người. và lưu ý var i = 1000; for (i; i; i--) {}var b =1000 for (b; b--;) {}nơi tôi đi 1000 đến 1 và b đi 999 đến 0. - trình duyệt càng cũ, càng có xu hướng ủng hộ hiệu suất.
Mark Schultheiss

9
Bạn cũng có thể khéo léo. for(var i = 0, l = myArray.length; i < l; ++i) ...là nhanh nhất và tốt nhất bạn có thể nhận được với lần lặp về phía trước.
Mathieu Amiot

Câu trả lời:


1557

Lý do là một cấu trúc:

var a = []; // Create a new empty array.
a[5] = 5;   // Perfectly legal JavaScript that resizes the array.

for (var i = 0; i < a.length; i++) {
    // Iterate over numeric indexes from 0 to 5, as everyone expects.
    console.log(a[i]);
}

/* Will display:
   undefined
   undefined
   undefined
   undefined
   undefined
   5
*/

đôi khi có thể hoàn toàn khác với khác:

var a = [];
a[5] = 5;
for (var x in a) {
    // Shows only the explicitly set index of "5", and ignores 0-4
    console.log(x);
}

/* Will display:
   5
*/

Cũng xem xét rằng các thư viện JavaScript có thể làm những việc như thế này, điều này sẽ ảnh hưởng đến bất kỳ mảng nào bạn tạo:

// Somewhere deep in your JavaScript library...
Array.prototype.foo = 1;

// Now you have no idea what the below code will do.
var a = [1, 2, 3, 4, 5];
for (var x in a){
    // Now foo is a part of EVERY array and 
    // will show up here as a value of 'x'.
    console.log(x);
}

/* Will display:
   0
   1
   2
   3
   4
   foo
*/


146
Trong lịch sử, một số trình duyệt thậm chí lặp lại theo 'độ dài', 'toString', v.v.!
bobince

398
Hãy nhớ sử dụng (var x in a)chứ không phải (x in a)- không muốn tạo ra một toàn cầu.
Chris Morgan

78
Vấn đề đầu tiên không phải là lý do nó xấu, chỉ là sự khác biệt về ngữ nghĩa. Vấn đề thứ hai đối với tôi là một lý do (trên các cuộc đụng độ giữa các thư viện làm tương tự) rằng việc thay đổi nguyên mẫu của một kiểu dữ liệu tích hợp là xấu, thay vì điều đó là..tôi là xấu.
Stewart

86
@Stewart: Tất cả các đối tượng trong JS là kết hợp. Mảng JS là một đối tượng, vì vậy, nó cũng có liên quan, nhưng đó không phải là mục đích của nó. Nếu bạn muốn lặp lại các khóa của đối tượng , hãy sử dụng for (var key in object). Tuy nhiên, nếu bạn muốn lặp lại các phần tử của một mảng , hãy sử dụng for(var i = 0; i < array.length; i += 1).
Martijn

42
Bạn đã nói ví dụ đầu tiên, rằng nó lặp lại trên các chỉ số số từ 0 đến 4, như mọi người mong đợi , tôi hy vọng nó sẽ lặp lại từ 0 đến 5 ! Vì nếu bạn thêm một phần tử ở vị trí 5, mảng sẽ có 6 phần tử (5 phần tử không xác định).
stivlo

393

Bản thân for-incâu lệnh không phải là một "thực hành xấu", tuy nhiên, nó có thể được sử dụng sai , ví dụ, để lặp lại trên các mảng hoặc các đối tượng giống như mảng.

Mục đích của for-intuyên bố là liệt kê các thuộc tính đối tượng. Tuyên bố này sẽ đi lên trong chuỗi nguyên mẫu, cũng liệt kê các thuộc tính được thừa kế , một điều mà đôi khi không mong muốn.

Ngoài ra, thứ tự lặp không được đảm bảo bởi thông số kỹ thuật, có nghĩa là nếu bạn muốn "lặp" một đối tượng mảng, với câu lệnh này, bạn không thể chắc chắn rằng các thuộc tính (chỉ mục mảng) sẽ được truy cập theo thứ tự số.

Ví dụ: trong JScript (IE <= 8), thứ tự liệt kê ngay cả trên các đối tượng Array được định nghĩa là các thuộc tính được tạo:

var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';

for (var p in array) {
  //... p will be "2", "1" and "0" on IE
}

Ngoài ra, nói về các thuộc tính được kế thừa, ví dụ, nếu bạn mở rộng Array.prototypeđối tượng (như một số thư viện như MooTools làm), các thuộc tính đó cũng sẽ được liệt kê:

Array.prototype.last = function () { return this[this.length-1]; };

for (var p in []) { // an empty array
  // last will be enumerated
}

Như tôi đã nói trước đây để lặp lại trên các mảng hoặc các đối tượng giống như mảng, điều tốt nhất là sử dụng một vòng lặp tuần tự , chẳng hạn như một vòng lặp for/ đơn giản while.

Khi bạn chỉ muốn liệt kê các thuộc tính riêng của một đối tượng (những thuộc tính không được kế thừa), bạn có thể sử dụng hasOwnPropertyphương thức:

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    // prop is not inherited
  }
}

Và một số người thậm chí khuyên bạn nên gọi phương thức trực tiếp từ Object.prototypeđể tránh gặp sự cố nếu ai đó thêm thuộc tính được đặt tên hasOwnPropertycho đối tượng của chúng tôi:

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }
}

10
Xem thêm bài đăng của David Humphrey Lặp lại các đối tượng trong JavaScript một cách nhanh chóng - vì các mảng for..inchậm hơn nhiều so với các vòng lặp "bình thường".
Chris Morgan

17
Câu hỏi về điểm cuối cùng về "hasOwnProperty": Nếu ai đó ghi đè "hasOwnProperty" trên một đối tượng, bạn sẽ gặp vấn đề. Nhưng bạn sẽ không gặp vấn đề tương tự nếu ai đó ghi đè "Object.prototype.hasOwnProperty"? Dù bằng cách nào họ đang làm phiền bạn và đó không phải là trách nhiệm của bạn phải không?
Scott Rippey

Bạn nói for..inkhông phải là thực hành xấu, nhưng nó có thể bị lạm dụng. Bạn đã có một ví dụ thực tế về thực hành tốt, nơi bạn thực sự muốn đi qua tất cả các thuộc tính đối tượng bao gồm các thuộc tính được kế thừa?
rjmunro

4
@ScottRippey: Nếu bạn muốn mang nó đến đó: youtube.com/watch?v=FrFUI591WhI
Tường Tường

với câu trả lời này, tôi thấy rằng có thể truy cập giá trị vớifor (var p in array) { array[p]; }
Equiman

117

Có ba lý do tại sao bạn không nên sử dụng for..inđể lặp lại các phần tử mảng:

  • for..insẽ lặp qua tất cả các thuộc tính riêng và được kế thừa của đối tượng mảng không có DontEnum; điều đó có nghĩa là nếu ai đó thêm thuộc tính vào đối tượng mảng cụ thể (có những lý do hợp lệ cho việc này - tôi đã tự mình làm như vậy) hoặc đã thay đổi Array.prototype(được coi là thực tiễn xấu trong mã được cho là hoạt động tốt với các tập lệnh khác), các thuộc tính này sẽ cũng được lặp đi lặp lại; Các thuộc tính được kế thừa có thể được loại trừ bằng cách kiểm tra hasOwnProperty(), nhưng điều đó sẽ không giúp bạn với các thuộc tính được đặt trong chính đối tượng mảng

  • for..in không được bảo đảm để giữ nguyên thứ tự

  • nó chậm vì bạn phải đi bộ tất cả các thuộc tính của đối tượng mảng và toàn bộ chuỗi nguyên mẫu của nó và vẫn sẽ chỉ lấy tên của thuộc tính, tức là để có được giá trị, sẽ cần phải tra cứu thêm


55

Bởi vì ... trong liệt kê thông qua đối tượng chứa mảng chứ không phải chính mảng đó. Nếu tôi thêm một hàm vào chuỗi nguyên mẫu mảng, thì nó cũng sẽ được bao gồm. I E

Array.prototype.myOwnFunction = function() { alert(this); }
a = new Array();
a[0] = 'foo';
a[1] = 'bar';
for(x in a){
 document.write(x + ' = ' + a[x]);
}

Điều này sẽ viết:

0 = foo
1 = thanh
myOwnFunction = function () {alert (này); }

Và vì bạn không bao giờ có thể chắc chắn rằng sẽ không có gì được thêm vào chuỗi nguyên mẫu, chỉ cần sử dụng vòng lặp for để liệt kê mảng:

for(i=0,x=a.length;i<x;i++){
 document.write(i + ' = ' + a[i]);
}

Điều này sẽ viết:

0 = foo
1 = thanh

16
Mảng các đối tượng, không có "đối tượng giữ mảng".
RobG

41

Trong sự cô lập, không có gì sai khi sử dụng for-in trên mảng. For-in lặp qua tên thuộc tính của một đối tượng và trong trường hợp mảng "ngoài luồng", các thuộc tính tương ứng với các chỉ mục mảng. (Các phần tử thích hợp được tích hợp sẵn length, toStringv.v. không được bao gồm trong phép lặp.)

Tuy nhiên, nếu mã của bạn (hoặc khung bạn đang sử dụng) thêm thuộc tính tùy chỉnh vào mảng hoặc vào nguyên mẫu mảng, thì các thuộc tính này sẽ được bao gồm trong lần lặp, có thể không phải là điều bạn muốn.

Một số khung công tác JS, như Prototype sửa đổi nguyên mẫu Array. Các khung công tác khác như JQuery không có, vì vậy với JQuery, bạn có thể sử dụng một cách an toàn.

Nếu bạn nghi ngờ, có lẽ bạn không nên sử dụng for-in.

Một cách khác để lặp qua một mảng là sử dụng vòng lặp for:

for (var ix=0;ix<arr.length;ix++) alert(ix);

Tuy nhiên, điều này có một vấn đề khác. Vấn đề là một mảng JavaScript có thể có "lỗ hổng". Nếu bạn xác định arrlà:

var arr = ["hello"];
arr[100] = "goodbye";

Sau đó, mảng có hai mục, nhưng độ dài 101. Sử dụng for-in sẽ mang lại hai chỉ mục, trong khi vòng lặp for sẽ mang lại 101 chỉ mục, trong đó 99 có giá trị undefined.


37

Kể từ năm 2016 (ES6), chúng tôi có thể sử dụng for…ofcho việc lặp mảng, như John Slegers đã nhận thấy.

Tôi chỉ muốn thêm mã trình diễn đơn giản này, để làm cho mọi thứ rõ ràng hơn:

Array.prototype.foo = 1;
var arr = [];
arr[5] = "xyz";

console.log("for...of:");
var count = 0;
for (var item of arr) {
    console.log(count + ":", item);
    count++;
    }

console.log("for...in:");
count = 0;
for (var item in arr) {
    console.log(count + ":", item);
    count++;
    }

Bảng điều khiển hiển thị:

for...of:

0: undefined
1: undefined
2: undefined
3: undefined
4: undefined
5: xyz

for...in:

0: 5
1: foo

Nói cách khác:

  • for...ofđếm từ 0 đến 5, và cũng bỏ qua Array.prototype.foo. Nó hiển thị các giá trị mảng .

  • for...inchỉ liệt kê 5, bỏ qua các chỉ mục mảng không xác định, nhưng thêm foo. Nó hiển thị tên thuộc tính mảng .


32

Câu trả lời ngắn gọn: Chỉ là không đáng.


Câu trả lời dài hơn: Chỉ là không đáng, ngay cả khi thứ tự phần tử tuần tự và hiệu suất tối ưu không được yêu cầu.


Câu trả lời dài: Chỉ là không đáng ...

  • Việc sử dụng for (var property in array)sẽ gây ra arraylặp đi lặp lại như một đối tượng , đi qua chuỗi nguyên mẫu đối tượng và cuối cùng thực hiện chậm hơn một forvòng lặp dựa trên chỉ mục .
  • for (... in ...) không được đảm bảo để trả về các thuộc tính đối tượng theo thứ tự liên tục, như người ta có thể mong đợi.
  • Việc sử dụng hasOwnProperty()!isNaN()kiểm tra để lọc các thuộc tính đối tượng là một chi phí bổ sung khiến nó hoạt động chậm hơn và phủ nhận lý do chính để sử dụng nó ở vị trí đầu tiên, tức là do định dạng ngắn gọn hơn.

Vì những lý do này, sự đánh đổi chấp nhận được giữa hiệu suất và sự tiện lợi thậm chí không tồn tại. Thực sự không có lợi ích gì trừ khi mục đích là xử lý mảng như một đối tượng và thực hiện các hoạt động trên các thuộc tính đối tượng của mảng.


31

Ngoài các lý do được đưa ra trong các câu trả lời khác, bạn có thể không muốn sử dụng cấu trúc "for ... in" nếu bạn cần làm toán với biến đếm vì vòng lặp lặp qua tên của các thuộc tính của đối tượng và do đó biến là một chuỗi.

Ví dụ,

for (var i=0; i<a.length; i++) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

se viêt

0, number, 1
1, number, 2
...

trong khi,

for (var ii in a) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

se viêt

0, string, 01
1, string, 11
...

Tất nhiên, điều này có thể dễ dàng khắc phục bằng cách bao gồm

ii = parseInt(ii);

trong vòng lặp, nhưng cấu trúc đầu tiên là trực tiếp hơn.


6
Bạn có thể sử dụng tiền tố +thay vì parseInttrừ khi bạn thực sự cần số nguyên hoặc bỏ qua các ký tự không hợp lệ.
Konrad Borowski

Ngoài ra, sử dụng parseInt()không được khuyến khích. Hãy thử parseInt("025");và nó sẽ thất bại.
Derek 朕 會

6
@Derek 會 - bạn chắc chắn có thể sử dụng parseInt. Vấn đề là nếu bạn không bao gồm cơ số, các trình duyệt cũ hơn có thể cố gắng diễn giải số (do đó 025 trở thành số bát phân). Điều này đã được sửa trong ECMAScript 5 nhưng nó vẫn xảy ra đối với các số bắt đầu bằng "0x" (nó diễn giải số là hex). Để đảm bảo an toàn, hãy sử dụng cơ số để chỉ định số như vậy parseInt("025", 10)- chỉ định cơ sở 10.
IAmTimCorey

23

Ngoài thực tế là for... invòng lặp trên tất cả các thuộc tính có thể đếm được ( không giống với "tất cả các phần tử mảng"!), Xem http: //www.ecma-i Intl.org/publications/files/ECMA-ST/Ecma -262.pdf , phần 12.6.4 (phiên bản 5) hoặc 13.7.5.15 (phiên bản thứ 7):

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

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

Điều đó có nghĩa là nếu một trình duyệt muốn, nó có thể đi qua các thuộc tính theo thứ tự mà chúng được chèn vào. Hoặc theo thứ tự số. Hoặc theo thứ tự từ vựng (trong đó "30" xuất hiện trước "4"! Hãy ghi nhớ tất cả các khóa đối tượng - và do đó, tất cả các chỉ mục mảng - thực sự là các chuỗi, do đó có nghĩa hoàn toàn). Nó có thể đi qua chúng bằng xô, nếu nó triển khai các đối tượng dưới dạng bảng băm. Hoặc lấy bất kỳ thứ gì trong số đó và thêm "ngược". Một trình duyệt thậm chí có thể lặp lại ngẫu nhiên và tuân thủ ECMA-262, miễn là nó truy cập mỗi thuộc tính chính xác một lần.

Trong thực tế, hầu hết các trình duyệt hiện muốn lặp theo thứ tự gần như nhau. Nhưng không có gì để nói họ phải làm. Đó là cách triển khai cụ thể và có thể thay đổi bất cứ lúc nào nếu một cách khác được tìm thấy là hiệu quả hơn nhiều.

Dù bằng cách nào, for... inmang theo nó không có ý nghĩa về trật tự. Nếu bạn quan tâm đến thứ tự, hãy rõ ràng về nó và sử dụng một forvòng lặp thông thường với một chỉ mục.


18

Chủ yếu là hai lý do:

Một

Giống như những người khác đã nói, Bạn có thể nhận được các khóa không có trong mảng của bạn hoặc được kế thừa từ nguyên mẫu. Vì vậy, nếu, giả sử, một thư viện thêm một thuộc tính vào các nguyên mẫu Array hoặc Object:

Array.prototype.someProperty = true

Bạn sẽ nhận được nó như là một phần của mỗi mảng:

for(var item in [1,2,3]){
  console.log(item) // will log 1,2,3 but also "someProperty"
}

bạn có thể giải quyết điều này bằng phương thức hasOwnProperty:

var ary = [1,2,3];
for(var item in ary){
   if(ary.hasOwnProperty(item)){
      console.log(item) // will log only 1,2,3
   }
}

nhưng điều này đúng với việc lặp lại bất kỳ đối tượng nào có vòng lặp for-in.

Hai

Thông thường thứ tự của các mục trong một mảng là quan trọng, nhưng vòng lặp for không nhất thiết phải lặp theo đúng thứ tự, đó là vì nó coi mảng là một đối tượng, đó là cách nó được triển khai trong JS, và không như một mảng. Điều này có vẻ như là một điều nhỏ, nhưng nó thực sự có thể làm hỏng các ứng dụng và rất khó để gỡ lỗi.


2
Object.keys(a).forEach( function(item) { console.log(item) } )Lặp lại một mảng các khóa thuộc tính riêng, không phải các khóa được kế thừa từ nguyên mẫu.
Qwerty

2
Đúng, nhưng giống như vòng lặp for-in, nó không nhất thiết phải theo đúng thứ tự chỉ mục. Ngoài ra, nó sẽ không hoạt động trên các trình duyệt cũ không hỗ trợ ES5.
Lior

Bạn có thể dạy các trình duyệt đó array.forEachbằng cách chèn một số mã nhất định vào tập lệnh của mình. Xem nhà phát triển
Qwerty

Tất nhiên, nhưng sau đó bạn đang thao túng nguyên mẫu, và đó không phải lúc nào cũng là một ý tưởng hay ... và vẫn vậy, bạn có vấn đề về trật tự ...
Lior

Và, tất nhiên, lý do số ba: mảng thưa thớt.
một oliver tốt hơn

16

Bởi vì nó liệt kê thông qua các trường đối tượng, không phải chỉ mục. Bạn có thể nhận được giá trị với chỉ số "độ dài" và tôi nghi ngờ bạn muốn điều này.


Vì vậy, cách tốt nhất để làm điều đó là gì?
lYriCAlsSH

3
for (var i = 0; i <Array.length; i ++) {}
vava

3
Trong firefox 3, bạn cũng có thể sử dụng cả Array.forEach hoặc for (var [i, v] trong Iterator (Array)) {} nhưng cả hai đều không hoạt động trong IE, mặc dù bạn có thể tự viết phương thức forEach.
vava

và hầu như mọi thư viện cũng có phương pháp riêng cho việc này.
vava

5
Câu trả lời này là sai. "chiều dài" sẽ không được bao gồm trong lần lặp for-in. Nó chỉ là tài sản bạn thêm mình được bao gồm.
JacquesB

16

Tôi không nghĩ rằng tôi có nhiều thứ để thêm vào. Câu trả lời của Triptych hoặc câu trả lời của CMS về lý do tại sao for...innên tránh sử dụng trong một số trường hợp.

Tuy nhiên, tôi muốn nói thêm rằng trong các trình duyệt hiện đại, có một giải pháp thay thế for...incó thể được sử dụng trong những trường hợp for...inkhông thể sử dụng. Sự thay thế đó là for...of:

for (var item of items) {
    console.log(item);
}

Ghi chú :

Thật không may, không có phiên bản Internet Explorer nào hỗ trợ for...of( Edge 12+ ), vì vậy bạn sẽ phải chờ thêm một thời gian nữa cho đến khi bạn có thể sử dụng nó trong mã sản xuất phía máy khách của mình. Tuy nhiên, nên an toàn khi sử dụng mã JS phía máy chủ của bạn (nếu bạn sử dụng Node.js ).


@georgeawg Ý bạn là for-ofkhông for-inphải không?
ᆼ ᆺ

15

Vấn đề với for ... in ...- và điều này chỉ trở thành vấn đề khi lập trình viên không thực sự hiểu ngôn ngữ; nó không thực sự là một lỗi hay bất cứ điều gì - là nó lặp đi lặp lại trên tất cả các thành viên của một đối tượng (tốt, tất cả các thành viên có thể đếm được , nhưng đó là một chi tiết cho đến bây giờ). Khi bạn muốn lặp lại chỉ các thuộc tính được lập chỉ mục của một mảng, cách duy nhất được đảm bảo để giữ mọi thứ nhất quán về mặt ngữ nghĩa là sử dụng một chỉ số nguyên (nghĩa là một for (var i = 0; i < array.length; ++i)vòng lặp kiểu).

Bất kỳ đối tượng có thể có các thuộc tính tùy ý liên quan đến nó. Cụ thể, sẽ không có gì khủng khiếp khi tải các thuộc tính bổ sung vào một thể hiện mảng, đặc biệt. Mã muốn chỉ nhìn thấy các thuộc tính giống như mảng được lập chỉ mục do đó phải bám vào một chỉ mục số nguyên. Mã nhận thức đầy đủ về những gì for ... inkhông và thực sự cần phải xem tất cả các thuộc tính, vậy thì cũng được.


Giải thích hay. Chỉ tò mò thôi. Nếu tôi có một mảng nằm trong một đối tượng dưới các thuộc tính nhân và đã làm for in, so với một vòng lặp thông thường, các mảng đó sẽ bị lặp lại? (về bản chất, hiệu suất chậm, phải không?)
NiCk Newman

2
@NiCkNewman cũng là đối tượng bạn tham chiếu sau inmột for ... invòng lặp sẽ chỉ
Pointy

Tôi hiểu rồi. Chỉ tò mò vì tôi có các đối tượng và mảng bên trong đối tượng trò chơi chính của mình và tự hỏi liệu việc nhập liệu có gây đau đớn hơn không, chỉ đơn giản là một vòng lặp thông thường trên các chỉ mục.
NiCk Newman

@NiCkNewman cũng chủ đề của toàn bộ câu hỏi này là bạn không nên sử dụng for ... intrên mảng; Có nhiều lý do tốt để không. Đây không phải là vấn đề hiệu suất quá lớn như là một vấn đề "đảm bảo nó không bị hỏng".
Pointy

Chà, các đối tượng của tôi được lưu trữ trong một mảng về mặt kỹ thuật, đó là lý do tại sao tôi lo lắng, đại loại như : [{a:'hey',b:'hi'},{a:'hey',b:'hi'}], nhưng phải, tôi hiểu.
NiCk Newman

9

Ngoài ra, do ngữ nghĩa, cách for, inxử lý các mảng (nghĩa là giống như bất kỳ đối tượng JavaScript nào khác) không được liên kết với các ngôn ngữ phổ biến khác.

// C#
char[] a = new char[] {'A', 'B', 'C'};
foreach (char x in a) System.Console.Write(x); //Output: "ABC"

// Java
char[] a = {'A', 'B', 'C'};
for (char x : a) System.out.print(x);          //Output: "ABC"

// PHP
$a = array('A', 'B', 'C');
foreach ($a as $x) echo $x;                    //Output: "ABC"

// JavaScript
var a = ['A', 'B', 'C'];
for (var x in a) document.write(x);            //Output: "012"

9

TL & DR: Sử dụng for invòng lặp trong mảng không phải là xấu, thực tế hoàn toàn ngược lại.

Tôi nghĩ rằng for invòng lặp là một viên ngọc của JS nếu được sử dụng đúng trong các mảng. Bạn sẽ có toàn quyền kiểm soát phần mềm của bạn và biết bạn đang làm gì. Chúng ta hãy xem những nhược điểm đã đề cập và từ chối từng cái một.

  1. Nó cũng lặp qua các thuộc tính được kế thừa: Trước hết, bất kỳ tiện ích mở rộng nào Array.prototypecần được thực hiện bằng cách sử dụng Object.defineProperty()enumerablemô tả của chúng phải được đặt thành false. Bất kỳ thư viện không làm như vậy không nên được sử dụng ở tất cả.
  2. Các thuộc tính mà bạn thêm vào chuỗi thừa kế sau này sẽ được tính: Khi thực hiện phân lớp mảng theo Object.setPrototypeOfhoặc theo Class extend. Object.defineProperty()Theo mặc định writable, bạn nên sử dụng lại bộ mô tả thuộc tính enumerablevà thuộc configurabletính false. Hãy xem ví dụ phân loại mảng ở đây ...

function Stack(...a){
  var stack = new Array(...a);
  Object.setPrototypeOf(stack, Stack.prototype);
  return stack;
}
Stack.prototype = Object.create(Array.prototype);                                 // now stack has full access to array methods.
Object.defineProperty(Stack.prototype,"constructor",{value:Stack});               // now Stack is a proper constructor
Object.defineProperty(Stack.prototype,"peak",{value: function(){                  // add Stack "only" methods to the Stack.prototype.
                                                       return this[this.length-1];
                                                     }
                                             });
var s = new Stack(1,2,3,4,1);
console.log(s.peak());
s[s.length] = 7;
console.log("length:",s.length);
s.push(42);
console.log(JSON.stringify(s));
console.log("length:",s.length);

for(var i in s) console.log(s[i]);

Vì vậy, bạn thấy .. for invòng lặp bây giờ an toàn kể từ khi bạn quan tâm đến mã của bạn.

  1. Các for invòng lặp là chậm: Địa ngục không. Đây là phương pháp lặp nhanh nhất nếu bạn lặp đi lặp lại trên các mảng thưa thớt cần thiết theo thời gian. Đây là một trong những thủ thuật hiệu suất quan trọng nhất mà người ta nên biết. Hãy xem một ví dụ. Chúng tôi sẽ lặp qua một mảng thưa thớt.

var a = [];
a[0] = "zero";
a[10000000] = "ten million";
console.time("for loop on array a:");
for(var i=0; i < a.length; i++) a[i] && console.log(a[i]);
console.timeEnd("for loop on array a:");
console.time("for in loop on array a:");
for(var i in a) a[i] && console.log(a[i]);
console.timeEnd("for in loop on array a:");


@Ravi Shanker Reddy Thiết lập điểm chuẩn tốt. Như tôi đã đề cập trong câu trả lời của mình, for invòng lặp vượt trội hơn các mảng khác "nếu" thì thưa thớt và càng nhiều nếu nó có kích thước lớn hơn. Vì vậy, tôi đã sắp xếp lại bài kiểm tra băng ghế cho một mảng thưa thớt arr, có kích thước ~ 10000 chỉ với 50 mục được chọn ngẫu nhiên trong số [42,"test",{t:1},null, void 0]các chỉ số ngẫu nhiên. Bạn sẽ nhận thấy ngay sự khác biệt. - >> Kiểm tra nó ở đây << - .
Redu

8

Ngoài các vấn đề khác, cú pháp "for..in" có thể chậm hơn, vì chỉ mục là một chuỗi, không phải là số nguyên.

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'

Có lẽ không có vấn đề gì cả. Các phần tử mảng là các thuộc tính của một đối tượng dựa trên Mảng hoặc giống như Mảng và tất cả các thuộc tính đối tượng đều có các khóa chuỗi. Trừ khi công cụ JS của bạn bằng cách nào đó tối ưu hóa nó, ngay cả khi bạn đã sử dụng một số, cuối cùng nó sẽ bị biến thành một chuỗi để tra cứu.
cHao

Bất kể mọi vấn đề về hiệu suất, nếu bạn chưa quen với JavaScript, hãy sử dụng var i in avà mong muốn chỉ mục là một số nguyên, sau đó thực hiện một thao tác nào đó a[i+offset] = <value>sẽ đặt các giá trị ở vị trí hoàn toàn sai. ("1" + 1 == "11").
szmoore

8

Một khía cạnh quan trọng là for...inchỉ lặp lại các thuộc tính có trong một đối tượng có thuộc tính thuộc tính vô số của chúng được đặt thành đúng. Vì vậy, nếu một người cố gắng lặp lại một đối tượng bằng cách sử dụng các thuộc tính tùy ý có thể bị bỏ lỡ nếu thuộc tính thuộc tính liệt kê của họ là sai. Hoàn toàn có thể thay đổi thuộc tính thuộc tính cho các đối tượng Array bình thường để các phần tử nhất định không được liệt kê. Mặc dù nói chung, các thuộc tính thuộc tính có xu hướng áp dụng cho các thuộc tính hàm trong một đối tượng.for...in

Người ta có thể kiểm tra giá trị của thuộc tính thuộc tính có thể đếm được của một thuộc tính bằng cách:

myobject.propertyIsEnumerable('myproperty')

Hoặc để có được tất cả bốn thuộc tính:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

Đây là một tính năng có sẵn trong ECMAScript 5 - trong các phiên bản trước đó, không thể thay đổi giá trị của thuộc tính thuộc tính có thể đếm được (nó luôn được đặt thành đúng).


8

for/ Hoạt inđộng với hai loại biến: hashtables (mảng kết hợp) và mảng (không liên kết).

JavaScript sẽ tự động xác định cách nó đi qua các mục. Vì vậy, nếu bạn biết rằng mảng của bạn thực sự không liên kết, bạn có thể sử dụng for (var i=0; i<=arrayLen; i++)và bỏ qua bước lặp tự động phát hiện.

Nhưng theo tôi, tốt hơn là sử dụng for/ in, quá trình cần thiết cho việc tự động phát hiện đó là rất nhỏ.

Một câu trả lời thực sự cho điều này sẽ phụ thuộc vào cách trình phân tích cú pháp trình duyệt / diễn giải mã JavaScript. Nó có thể thay đổi giữa các trình duyệt.

Tôi không thể nghĩ ra các mục đích khác để không sử dụng for/ in;

//Non-associative
var arr = ['a', 'b', 'c'];
for (var i in arr)
   alert(arr[i]);

//Associative
var arr = {
   item1 : 'a',
   item2 : 'b',
   item3 : 'c'
};

for (var i in arr)
   alert(arr[i]);

đúng, trừ khi bạn đang sử dụng các đối tượng nguyên mẫu. ;) bên dưới
Ricardo

Đó Arraylà vì Objectquá
Tư vấn miễn phí

2
for ... inlàm việc với các đối tượng. Không có thứ gọi là tự động phát hiện.
một oliver tốt hơn

7

Bởi vì nó sẽ lặp lại các thuộc tính thuộc về các đối tượng trong chuỗi nguyên mẫu nếu bạn không cẩn thận.

Bạn có thể sử dụng for.. in, chỉ cần chắc chắn kiểm tra từng thuộc tính với hasOwnProperty .


2
Chưa đủ - hoàn toàn ổn khi thêm các thuộc tính được đặt tên tùy ý vào các trường hợp mảng và chúng sẽ kiểm tra truetừ các hasOwnProperty()kiểm tra.
Mũi nhọn

Điểm tốt, cảm ơn. Tôi chưa bao giờ đủ ngớ ngẩn để làm điều đó với một mảng, vì vậy tôi đã không xem xét điều đó!
JAL

1
@Pointy Tôi chưa kiểm tra điều này, nhưng có lẽ điều này có thể khắc phục bằng cách sử dụng isNaNkiểm tra trên mỗi tên thuộc tính.
WynandB

1
@Wynand ý tưởng thú vị; tuy nhiên tôi không thực sự thấy lý do tại sao nó đáng để gặp rắc rối khi lặp lại với một chỉ số số đơn giản lại quá dễ dàng.
Mũi nhọn

@WynandB xin lỗi vì vết sưng, nhưng tôi cảm thấy có một sự điều chỉnh theo thứ tự: isNaNlà để kiểm tra xem một biến có phải là giá trị đặc biệt NaN hay không, nó không thể được sử dụng để kiểm tra 'những thứ khác ngoài số' (bạn có thể đi với một số thông thường loại cho điều đó).
doldt

6

Nó không hẳn là xấu (dựa trên những gì bạn đang làm), nhưng trong trường hợp mảng, nếu một cái gì đó đã được thêm vào Array.prototype, thì bạn sẽ nhận được kết quả lạ. Nơi bạn muốn vòng lặp này chạy ba lần:

var arr = ['a','b','c'];
for (var key in arr) { ... }

Nếu một hàm gọi là helpfulUtilityMethodđã được thêm vào Array's prototype, sau đó vòng lặp của bạn sẽ kết thúc chạy gấp bốn lần: keysẽ 0, 1, 2, và helpfulUtilityMethod. Nếu bạn chỉ mong đợi số nguyên, rất tiếc.


6

Bạn chỉ nên sử dụng for(var x in y)trên danh sách tài sản, không phải trên các đối tượng (như đã giải thích ở trên).


13
Chỉ cần một lưu ý về SO - không có 'ở trên' vì các bình luận thay đổi thứ tự trên trang mọi lúc. Vì vậy, chúng tôi không thực sự biết ý kiến ​​của bạn. Thật tốt khi nói "trong bình luận của người này" vì lý do này.
JAL

@JAL ... hoặc thêm permalink vào câu trả lời.
WynandB

5

Sử dụng for...invòng lặp cho một mảng không sai, mặc dù tôi có thể đoán tại sao ai đó nói với bạn rằng:

1.) Đã có một hàm hoặc phương thức bậc cao hơn, có mục đích đó cho một mảng, nhưng có nhiều chức năng hơn và cú pháp gọn hơn, được gọi là 'forEach': Array.prototype.forEach(function(element, index, array) {} );

2.) Mảng luôn luôn có một độ dài, nhưng for...inforEachkhông thực hiện một chức năng đối với bất kỳ giá trị đó là 'undefined', chỉ dành cho các chỉ số có giá trị xác định. Vì vậy, nếu bạn chỉ gán một giá trị, các vòng lặp này sẽ chỉ thực hiện một hàm một lần, nhưng vì một mảng được liệt kê, nó sẽ luôn có độ dài lên đến chỉ mục cao nhất có giá trị xác định, nhưng độ dài đó có thể không được chú ý khi sử dụng các giá trị này vòng lặp.

3.) Tiêu chuẩn cho vòng lặp sẽ thực thi một hàm nhiều lần như bạn xác định trong các tham số và vì một mảng được đánh số, nên có ý nghĩa hơn để xác định số lần bạn muốn thực hiện một hàm. Không giống như các vòng lặp khác, vòng lặp for sau đó có thể thực thi một hàm cho mọi chỉ mục trong mảng, cho dù giá trị được xác định hay không.

Về bản chất, bạn có thể sử dụng bất kỳ vòng lặp nào, nhưng bạn nên nhớ chính xác cách chúng hoạt động. Hiểu các điều kiện mà các vòng lặp khác nhau nhắc lại, các chức năng riêng biệt của chúng và nhận ra chúng sẽ ít nhiều phù hợp với các kịch bản khác nhau.

Ngoài ra, nó có thể được coi là một cách sử dụng forEachphương pháp tốt hơn so với for...invòng lặp nói chung, bởi vì nó dễ viết hơn và có nhiều chức năng hơn, vì vậy bạn có thể muốn có thói quen chỉ sử dụng phương pháp này và tiêu chuẩn cho, nhưng gọi.

Xem bên dưới rằng hai vòng lặp đầu tiên chỉ thực hiện các câu lệnh console.log một lần, trong khi vòng lặp tiêu chuẩn thực thi hàm nhiều lần như đã chỉ định, trong trường hợp này, mảng.length = 6.

var arr = [];
arr[5] = 'F';

for (var index in arr) {
console.log(index);
console.log(arr[index]);
console.log(arr)
}
// 5
// 'F'
// => (6) [undefined x 5, 6]

arr.forEach(function(element, index, arr) {
console.log(index);
console.log(element);
console.log(arr);
});
// 5
// 'F'
// => Array (6) [undefined x 5, 6]

for (var index = 0; index < arr.length; index++) {
console.log(index);
console.log(arr[index]);
console.log(arr);
};
// 0
// undefined
// => Array (6) [undefined x 5, 6]

// 1
// undefined
// => Array (6) [undefined x 5, 6]

// 2
// undefined
// => Array (6) [undefined x 5, 6]

// 3
// undefined
// => Array (6) [undefined x 5, 6]

// 4
// undefined
// => Array (6) [undefined x 5, 6]

// 5
// 'F'
// => Array (6) [undefined x 5, 6]

4

Dưới đây là những lý do tại sao điều này (thường) là một thực tiễn xấu:

  1. for...incác vòng lặp lặp đi lặp lại trên tất cả các thuộc tính vô số của riêng chúng các thuộc tính vô số của nguyên mẫu của chúng. Thông thường trong một lần lặp mảng, chúng ta chỉ muốn lặp lại trên chính mảng đó. Và mặc dù bản thân bạn có thể không thêm bất cứ thứ gì vào mảng, thư viện hoặc khung của bạn có thể thêm một cái gì đó.

Ví dụ :

Array.prototype.hithere = 'hithere';

var array = [1, 2, 3];
for (let el in array){
    // the hithere property will also be iterated over
    console.log(el);
}

  1. for...incác vòng lặp không đảm bảo một thứ tự lặp cụ thể . Mặc dù thứ tự thường thấy trong hầu hết các trình duyệt hiện đại ngày nay, nhưng vẫn không có gì đảm bảo 100%.
  2. for...incác vòng lặp bỏ qua undefinedcác phần tử mảng, tức là các phần tử mảng chưa được gán.

Ví dụ ::

const arr = []; 
arr[3] = 'foo';   // resize the array to 4
arr[4] = undefined; // add another element with value undefined to it

// iterate over the array, a for loop does show the undefined elements
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

console.log('\n');

// for in does ignore the undefined elements
for (let el in arr) {
    console.log(arr[el]);
}


2

for ... in rất hữu ích khi làm việc trên một đối tượng trong JavaScript, nhưng không phải cho một mảng, nhưng chúng ta vẫn không thể nói đó là một cách sai, nhưng nó không được khuyến khích, hãy xem ví dụ dưới đây bằng cách sử dụng ... trong vòng lặp:

let txt = "";
const person = {fname:"Alireza", lname:"Dezfoolian", age:35}; 
for (const x in person) {
    txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

OK, hãy làm điều đó với Array ngay bây giờ:

let txt = "";
const person = ["Alireza", "Dezfoolian", 35]; 
for (const x in person) {
   txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

Như bạn thấy kết quả giống nhau ...

Nhưng hãy thử một cái gì đó, hãy tạo nguyên mẫu một cái gì đó cho Mảng ...

Array.prototype.someoneelse = "someoneelse";

Bây giờ chúng ta tạo một Array mới ();

let txt = "";
const arr = new Array();
arr[0] = 'Alireza';
arr[1] = 'Dezfoolian';
arr[2] = 35;
for(x in arr) {
 txt += arr[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 someoneelse

Bạn thấy someoneelse !!! ... Chúng tôi thực sự Looping qua đối tượng Array mới trong trường hợp này!

Vì vậy, đó là một trong những lý do tại sao chúng ta cần phải sử dụng for..in cẩn thận, nhưng nó không phải luôn luôn như vậy ...


2

A cho ... trong vòng lặp luôn liệt kê các phím. Các khóa thuộc tính của đối tượng luôn là String, thậm chí là các thuộc tính được lập chỉ mục của một mảng:

var myArray = ['a', 'b', 'c', 'd'];
var total = 0
for (elem in myArray) {
  total += elem
}
console.log(total); // 00123


0

mặc dù không được giải quyết cụ thể bằng câu hỏi này, tôi sẽ nói thêm rằng có một lý do rất chính đáng để không sử dụng cho ... với một NodeList(như người ta sẽ nhận được từ một querySelectorAllcuộc gọi, vì nó hoàn toàn không thấy các phần tử được trả về lặp chỉ qua các thuộc tính NodeList.

trong trường hợp của một kết quả duy nhất, tôi đã nhận được:

var nodes = document.querySelectorAll(selector);
nodes
 NodeList [a._19eb]
for (node in nodes) {console.log(node)};
VM505:1 0
VM505:1 length
VM505:1 item
VM505:1 entries
VM505:1 forEach
VM505:1 keys
VM505:1 values

Điều đó giải thích tại sao tôi for (node in nodes) node.href = newLink;thất bại.

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.