Câu hỏi Lặp qua một mảng trong JavaScript


Trong Java, bạn có thể sử dụng for vòng lặp để duyệt các đối tượng trong một mảng như sau:

String[] myStringArray = {"Hello", "World"};
for (String s : myStringArray)
{
    // Do something
}

Bạn có thể làm tương tự trong JavaScript?


2421
2018-06-10 00:04


gốc


Ok, do đó, tôi là một chút bối rối, nó ok để sử dụng tăng cường cho vòng lặp khi bạn đang truy cập các đối tượng? Và sử dụng một tuần tự cho điền một? Điều này có đúng không? - Mark Szymanski
không, nó thực sự đơn giản, các đối tượng mảng có chỉ mục số, vì vậy bạn muốn lặp lại trên các chỉ mục đó theo thứ tự số, một vòng tuần tự đảm bảo rằng, tăng cường  for-in vòng lặp liệt kê các thuộc tính đối tượng, không có thứ tự cụ thể và nó cũng liệt kê các thuộc tính kế thừa ... cho lặp lại qua các vòng lặp tuần tự được đề xuất ... - CMS
liên quan - stackoverflow.com/questions/5349425/… - jondavidjohn
liên quan - stackoverflow.com/q/6208964/31671 - alex
jsben.ch/#/Q9oD5 <= Đây là điểm chuẩn của một loạt các giải pháp cho lặp qua mảng - EscapeNetscape


Các câu trả lời:


Sử dụng tuần tự for vòng lặp:

var myStringArray = ["Hello","World"];
var arrayLength = myStringArray.length;
for (var i = 0; i < arrayLength; i++) {
    alert(myStringArray[i]);
    //Do something
}

@zipcodeman đề xuất việc sử dụng for...in tuyên bố, nhưng để lặp mảng for-in nên tránh, tuyên bố đó có nghĩa là liệt kê thuộc tính đối tượng.

Nó không nên được sử dụng cho các đối tượng giống mảng vì:

  • Thứ tự lặp lại không được đảm bảo, các chỉ mục mảng có thể không được truy cập theo thứ tự số.
  • Các thuộc tính kế thừa cũng được liệt kê.

Điểm thứ hai là nó có thể cung cấp cho bạn rất nhiều vấn đề, ví dụ, nếu bạn mở rộng Array.prototype đối tượng để bao gồm một phương thức ở đó, thuộc tính đó cũng sẽ được liệt kê.

Ví dụ:

Array.prototype.foo = "foo!";
var array = ['a', 'b', 'c'];

for (var i in array) {
  alert(array[i]);
}

Đoạn mã trên sẽ cảnh báo, "a", "b", "c" và "foo!".

Điều đó đặc biệt là một vấn đề nếu bạn sử dụng một số thư viện dựa chủ yếu vào việc tăng thêm các nguyên mẫu gốc (ví dụ như MooTools).

Các for-in tuyên bố như tôi đã nói trước đây là liệt kê thuộc tính đối tượng, ví dụ:

var obj = {
  "a": 1,
  "b": 2,
  "c": 3
};

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) { 
  // or if (Object.prototype.hasOwnProperty.call(obj,prop)) for safety...
    alert("prop: " + prop + " value: " + obj[prop])
  }
}

Trong ví dụ trên, hasOwnProperty phương pháp cho phép bạn liệt kê chỉ tài sản riêng, đó là nó, chỉ có các thuộc tính mà đối tượng vật lý có, không có các thuộc tính kế thừa.

Tôi sẽ khuyên bạn nên đọc bài viết sau:


3060
2018-06-10 00:07



Tại sao bỏ phiếu xuống? for...in nên là tránh cho các đối tượng giống như mảng! - CMS
Đây là lý do (bởi CMS anh ta tự) stackoverflow.com/questions/1885317/… - OscarRyz
@ DoubleGras, tôi nghĩ đó là một ý kiến ​​không được mọi người chia sẻ. Xem: stackoverflow.com/questions/5752906/… hoặc là groups.google.com/forum/?fromgroups#!topic/jsmentors/… - Matthijs Wessels
@StijndeWitt Không, bởi vì nó phá vỡ nếu bạn có bất kỳ "falsey" giá trị trong mảng của bạn: false, undefined, 0, "", NaN. - Phrogz
jsperf.com/caching-array-length/4  Đây là một thử nghiệm để xem nếu nó có giá trị bộ nhớ đệm chiều dài của một mảng trong một vòng lặp Javascript - Enrico


Có, nhưng chỉ khi triển khai của bạn bao gồm for...of tính năng được giới thiệu trong ECMAScript 2015 (bản phát hành "Harmony").

Nó hoạt động như thế này:

// REQUIRES ECMASCRIPT 2015+
var s, myStringArray = ["Hello", "World"];
for (s of myStringArray) {
  // ... do something with s ...
}

Hoặc tốt hơn, vì ECMAScript 2015 cũng cung cấp các biến có phạm vi khối thông qua let và const:

// REQUIRES ECMASCRIPT 2015+
const myStringArray = ["Hello", "World"];
for (const s of myStringArray) {
  // ... do something with s ...
}
// s is no longer defined here

Nhiều nhà phát triển JavaScript vẫn đang làm việc trong môi trường chưa có, tuy nhiên - đặc biệt nếu viết mã để chạy trong trình duyệt web, nơi các nhà phát triển trang web thường không thể chắc chắn trình duyệt / phiên bản nào mà khách hàng của họ sẽ sử dụng.

Nếu bạn có thể giả định trình thông dịch JavaScript tuân thủ Trước phiên bản của đặc tả ECMAScript (có quy tắc, ví dụ, các phiên bản của Internet Explorer trước 9), sau đó bạn có thể sử dụng forEach phương thức lặp thay vì một vòng lặp. Trong trường hợp đó, bạn chuyển một hàm được gọi trên mỗi mục trong mảng:

var myStringArray = [ "Hello", "World" ];
myStringArray.forEach( function(s) { 
     // ... do something with s ...
} );

Nhưng nếu đó là quá nhiều để giả định, và bạn muốn một cái gì đó mà làm việc trong tất cả các các phiên bản của JavaScript, sau đó bạn phải sử dụng một vòng lặp đếm rõ ràng. Phiên bản an toàn nhất, xử lý các mảng thưa thớt đúng cách, giống như sau:

var i, s, myStringArray = [ "Hello", "World" ], len = myStringArray.length;
for (i=0; i<len; ++i) {
  if (i in myStringArray) {
    s = myStringArray[i];
    // ... do something with s ...
  }
}

Gán giá trị độ dài cho biến cục bộ (trái ngược với việc bao gồm toàn bộ giá trị myStringArray.length biểu thức trong điều kiện vòng lặp) có thể tạo sự khác biệt đáng kể về hiệu suất vì nó bỏ qua tra cứu thuộc tính mỗi lần qua; sử dụng Rhino trên máy tính của tôi, tốc độ tăng lên là 43%.

Bạn sẽ thường thấy độ dài bộ nhớ đệm được thực hiện trong mệnh đề khởi tạo vòng lặp, như sau:

var i, len, myStringArray = [ "Hello", "World" ];
for (len = myStringArray.length, i=0; i<len; ++i) {

Các for...in cú pháp được đề cập bởi những người khác là để lặp qua các thuộc tính của đối tượng; vì một mảng trong JavaScript chỉ là một đối tượng có tên thuộc tính số (và được tự động cập nhật lengthtài sản), bạn về mặt lý thuyết có thể lặp lại một mảng với nó. Nhưng vấn đề là nó không tự giới hạn các giá trị thuộc tính số (hãy nhớ rằng ngay cả các phương thức thực sự chỉ là các thuộc tính có giá trị là đóng), cũng không lặp lại các giá trị đó theo thứ tự số. Do đó, for...in cú pháp nên không phải được sử dụng để lặp qua mảng.


871
2018-04-16 02:03



Lưu ý rằng một số thông dịch viên (ví dụ: V8) sẽ tự động lưu vào bộ nhớ cache độ dài của mảng nếu mã được gọi là đủ thời gian và nó phát hiện rằng độ dài không được sửa đổi bởi vòng lặp. Trong khi bộ nhớ đệm chiều dài vẫn còn tốt đẹp, nó có thể không cung cấp một tăng tốc độ khi mã của bạn đang được gọi đủ thời gian để thực sự tạo sự khác biệt. - Phrogz
@ mark-reed Bạn có thể giải thích lý do bạn đã sử dụng i in myStringArray trong ví dụ của bạn? Làm sao điều đó có thể sai được? - Denis V
@DenisV: sai. a=[1,2,3,4]; delete a[2]; for (j in a) { console.log(j); }  kết quả đầu ra 0, 1, 3 và 4. a.lengthvẫn còn 5. - Mark Reed
Tôi không đề xuất for j in a. Tôi đang chứng minh rằng in kiểm tra không phải là dư thừa, như bạn đã xác nhận, bằng cách hiển thị tất cả các chỉ mục và hiển thị rằng có một chỉ số từ 0 đến length-1 không có đó. Tôi cũng có thể vừa in 2 in a, thực sự false, mặc dù thực tế là bạn nói điều đó là không thể. - Mark Reed
@GrijeshChauhan - đúng. Ví dụ, IE thông qua phiên bản 8 không hỗ trợ nó. Xem câu hỏi này. - Mark Reed


Bạn có thể dùng map, là một kỹ thuật lập trình chức năng cũng có sẵn bằng các ngôn ngữ khác như Python và Haskell.

[1,2,3,4].map( function(item) {
     alert(item);
})

Cú pháp chung là:

array.map(func)

Nói chung func sẽ lấy một tham số, là một mục của mảng. Nhưng trong trường hợp JavaScript, nó có thể lấy tham số thứ hai là chỉ mục của mục và tham số thứ ba là chính mảng đó.

Giá trị trả về của array.map là một mảng khác, vì vậy bạn có thể sử dụng nó như sau:

var x = [1,2,3,4].map( function(item) {return item * 10;});

Và bây giờ x là [10,20,30,40].

Bạn không phải viết hàm nội tuyến. Nó có thể là một chức năng riêng biệt.

var item_processor = function(item) {
      // Do something complicated to an item
}

new_list = my_list.map(item_processor);

sẽ tương đương với:

 for (item in my_list) {item_processor(item);}

Ngoại trừ bạn không nhận được new_list.


392
2018-06-10 00:09



Không, nhưng nó có thể mạnh hơn. kiểm tra điều này: joelonsoftware.com/items/2006/08/01.html - hasen
Ví dụ cụ thể đó có thể được triển khai tốt hơn bằng cách sử dụng Array.forEach. map là để tạo một mảng mới. - harto
@hasen, Array.prototype.map là một phần của Tiêu chuẩn phiên bản 5 của ECMAScript, chưa có sẵn trên tất cả các triển khai (ví dụ: thiếu IE), cũng cho lặp lại trên một mảng Tôi nghĩ rằng Array.prototype.forEach phương pháp là nhiều hơn ngữ nghĩa chính xác ... cũng xin vui lòng không đề nghị cho-in tuyên bố, xem câu trả lời của tôi để biết thêm chi tiết :) - CMS
Sự khác biệt giữa forEach và map là trước đây không trả về kết quả của phép lặp. map (đôi khi a.k.a. collect, nhưng rất khác apply) là rõ ràng cho việc chuyển đổi từng phần tử của một mảng thành một kết quả tương ứng; đó là 1-to-1 lập bản đồ, do đó tên. Đó là một phần của toàn bộ gia đình hoạt động bao gồm reduce (tạo ra một kết quả duy nhất từ ​​toàn bộ mảng) và filter (trong đó tạo ra một tập hợp con của mảng ban đầu) và như vậy. Trong khi forEach chỉ cần làm một cái gì đó với mỗi yếu tố, ngữ nghĩa không xác định. - Mark Reed
Downvote vì nếu bạn không thực sự lập bản đồ một cái gì đó, sau đó sử dụng [] .map là gây hiểu nhầm. [] .forEach tạo ra ý nghĩa ngữ nghĩa và cũng truyền cùng ba đối số cho hàm. - gengkev


Trong JavaScript, không nên lặp qua một mảng với vòng lặp for-in, nhưng tốt hơn nên sử dụng vòng lặp for chẳng hạn như:

for(var i=0, len=myArray.length; i < len; i++){}

Nó cũng được tối ưu hóa ("caching" chiều dài mảng). Nếu bạn muốn tìm hiểu thêm, đọc bài viết của tôi về chủ đề này.


102
2017-12-07 07:24



myArray.forEach (hàm (obj) {}); vẫn là tốt nhất - Jan Sverre
một cải tiến nhỏ: bạn có thể sử dụng ++i thay vì i++ - roberkules
++i là một tối ưu hóa trường học cũ mà trình biên dịch hiện đại làm cho bạn trong một vòng lặp for từ một thời gian dài trước đây :) stackoverflow.com/a/1547433/1033348 - ngryman
@Jannis .forEach có một số điều chống lại nó. 1) không phải bản địa 2) Yêu cầu bối cảnh thực thi mới cho MỌI chỉ mục khá đắt tiền và có vẻ như quá mức cần thiết (xem dmitrysoshnikov.com/ecmascript/chapter-1-execution-contexts) - Jose
Bạn phải cẩn thận bằng cách sử dụng vòng lặp này. Tôi bắt đầu sử dụng nó và đã có một khó khăn để theo dõi lỗi vì một sai lầm tôi đã thực hiện. Nếu bạn lồng hai vòng như thế này: jsfiddle.net/KQwmL/1. Bạn phải cẩn thận để đặt tên var len khác nhau trong hai vòng, nếu không vòng lặp thứ hai sẽ ghi đè lên len đầu tiên. - Rui Marques


cho (var s của myStringArray) {

(Trực tiếp trả lời câu hỏi của bạn: bây giờ bạn có thể!)

Hầu hết các câu trả lời khác là đúng, nhưng họ không đề cập đến (như của văn bản này) rằng Tập lệnh ECMA 6 2015 mang lại một cơ chế mới để thực hiện lặp lại, for..of vòng lặp.

Cú pháp mới này là cách thanh lịch nhất để lặp lại một mảng trong javascript (miễn là bạn không cần chỉ mục lặp), nhưng nó chưa được các trình duyệt hỗ trợ rộng rãi.

Nó hiện đang làm việc với Firefox 13+, Chrome 37+ và nó không thực sự hoạt động với các trình duyệt khác (xem khả năng tương thích với trình duyệt bên dưới). May mắn là chúng ta có các trình biên dịch JS (chẳng hạn như Babel) cho phép chúng tôi sử dụng các tính năng thế hệ tiếp theo ngay hôm nay.

Nó cũng hoạt động trên Node (tôi đã thử nghiệm nó trên phiên bản 0.12.0).

Lặp lại một mảng

// You could also use "let" instead of "var" for block scope.
for (var letter of ["a", "b", "c"]) { 
   console.log(letter); 
}

Lặp lại một mảng các đối tượng

var band = [
  {firstName : 'John', lastName: 'Lennon'}, 
  {firstName : 'Paul', lastName: 'McCartney'}
];

for(var member of band){
  console.log(member.firstName + ' ' + member.lastName); 
}

Lặp lại một máy phát điện:

(ví dụ được trích xuất từ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)

function* fibonacci() { // a generator function
  let [prev, curr] = [1, 1];
  while (true) {
    [prev, curr] = [curr, prev + curr];
    yield curr;
  }
}

for (let n of fibonacci()) {
  console.log(n);
  // truncate the sequence at 1000
  if (n >= 1000) {
    break;
  }
}

Bảng tương thích: http://kangax.github.io/es5-compat-table/es6/#For..of loops

Spec:  http://wiki.ecmascript.org/doku.php?id=harmony:iterators

}


92
2017-08-11 15:54



Nếu bạn đang sử dụng ES6, tôi sẽ đề nghị const s thay vì var s - joeytwiddle


Opera, Safari, Firefox và Chrome bây giờ tất cả đều chia sẻ một tập hợp các phương thức Array nâng cao để tối ưu hóa nhiều vòng chung.

Bạn có thể không cần tất cả chúng, nhưng chúng có thể rất hữu ích, hoặc sẽ là nếu mọi trình duyệt hỗ trợ chúng.

Mozilla Labs đã xuất bản các thuật toán và WebKit cả hai đều sử dụng, để bạn có thể tự thêm chúng.

bộ lọc trả về một mảng các mục đáp ứng một số điều kiện hoặc kiểm tra.

mỗi trả về true nếu mọi thành viên mảng vượt qua bài kiểm tra.

một số trả về true nếu vượt qua bài kiểm tra.

cho mỗi chạy một hàm trên mỗi thành viên mảng và không trả về bất kỳ thứ gì.

bản đồ giống như forEach, nhưng nó trả về một mảng các kết quả của phép toán cho mỗi phần tử.

Tất cả các phương thức này lấy một hàm cho đối số đầu tiên của chúng và có đối số thứ hai tùy chọn, là một đối tượng có phạm vi mà bạn muốn áp đặt lên các thành viên mảng khi chúng lặp qua hàm.

Bỏ qua nó cho đến khi bạn cần nó.

Chỉ số và lastIndexOf tìm vị trí thích hợp của phần tử đầu tiên hoặc cuối cùng khớp chính xác với đối số của nó.

(function(){
    var p, ap= Array.prototype, p2={
        filter: function(fun, scope){
            var L= this.length, A= [], i= 0, val;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        val= this[i];
                        if(fun.call(scope, val, i, this)){
                            A[A.length]= val;
                        }
                    }
                    ++i;
                }
            }
            return A;
        },
        every: function(fun, scope){
            var L= this.length, i= 0;
            if(typeof fun== 'function'){
                while(i<L){
                    if(i in this && !fun.call(scope, this[i], i, this))
                        return false;
                    ++i;
                }
                return true;
            }
            return null;
        },
        forEach: function(fun, scope){
            var L= this.length, i= 0;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        fun.call(scope, this[i], i, this);
                    }
                    ++i;
                }
            }
            return this;
        },
        indexOf: function(what, i){
            i= i || 0;
            var L= this.length;
            while(i< L){
                if(this[i]=== what)
                    return i;
                ++i;
            }
            return -1;
        },
        lastIndexOf: function(what, i){
            var L= this.length;
            i= i || L-1;
            if(isNaN(i) || i>= L)
                i= L-1;
            else
                if(i< 0) i += L;
            while(i> -1){
                if(this[i]=== what)
                    return i;
                --i;
            }
            return -1;
        },
        map: function(fun, scope){
            var L= this.length, A= Array(this.length), i= 0, val;
            if(typeof fun== 'function'){
                while(i< L){
                    if(i in this){
                        A[i]= fun.call(scope, this[i], i, this);
                    }
                    ++i;
                }
                return A;
            }
        },
        some: function(fun, scope){
            var i= 0, L= this.length;
            if(typeof fun== 'function'){
                while(i<L){
                    if(i in this && fun.call(scope, this[i], i, this))
                        return true;
                    ++i;
                }
                return false;
            }
        }
    }
    for(p in p2){
        if(!ap[p])
            ap[p]= p2[p];
    }
    return true;
})();

81
2018-06-10 02:43



Ngoài ra: IE hỗ trợ forEach kể từ phiên bản 9, xem Phương pháp forEach MSDN - rwitzel


Sử dụng vòng lặp while ...

var i=0, item, items = ['one','two','three'];
while(item = items[i++]){
    console.log(item);
}

nhật ký: 'một', 'hai', 'ba'

Và đối với thứ tự ngược lại, một vòng lặp hiệu quả hơn

var items = ['one','two','three'], i = items.length;
while(i--){
    console.log(items[i]);
}

nhật ký: 'ba', 'hai', 'một'

Hay cổ điển for vòng lặp

var items = ['one','two','three']
for(var i=0, l = items.length; i < l; i++){
    console.log(items[i]);
}

nhật ký: 'một', 'hai', 'ba'

Tài liệu tham khảo: http://www.sitepoint.com/google-closure-how-not-to-write-javascript/


62
2018-01-05 09:15



Ví dụ đầu tiên của cú pháp "while" sẽ không hoạt động nếu bất kỳ phần tử mảng nào bị lỗi. - Chris Cooper
... và vòng lặp while này tương đương với: đối với (var i = 0, item; item = items [i]; i ++), sẽ loại bỏ nhu cầu khai báo các biến chỉ mục và mục trước ... - Stijn de Witt
@StijndeWitt Nhưng đối với điều này áp dụng: nếu giá trị là falsynó sẽ không hoạt động... - yckart
Trong khi lặp qua bộ sưu tập, bạn nên quan tâm đến việc tối ưu hóa. Vì nó có thể ảnh hưởng đến hiệu suất trang. - Zaheer Ahmed