Dành cho từng mảng trong JavaScript


4684

Làm cách nào tôi có thể lặp qua tất cả các mục trong một mảng bằng JavaScript?

Tôi nghĩ rằng nó là một cái gì đó như thế này:

forEach(instance in theArray)

Trong trường hợp theArraylà mảng của tôi, nhưng điều này có vẻ là không chính xác.


16
Tôi đã tìm kiếm nó, nhưng tôi đã tìm kiếm forEach và không chỉ for. như đã nói, trong c # thì hơi khác một chút và điều đó làm tôi bối rối :)
Dante1986

6
ECMAScript & nbsp; 6 có thể chứa cấu trúc "for ... of". Xem ... của (MDN) để biết thêm thông tin. Bạn đã có thể dùng thử với các phiên bản Firefox gần đây.
Slaven Rezic

36
Array.ForEach chậm hơn khoảng 95% so với for () cho mỗi Mảng trong JavaScript. Xem thử nghiệm hiệu suất này trực tuyến: jsperf.com/fast-array-foreach qua coderwall.com/p/kvzbpa
molokoloco

77
Đối với nhiều tình huống, chậm hơn 95% sẽ không phải là blog
David Sykes

7
Ngược lại, trong Python nó là hiệu quả hơn để sử dụng chức năng hơn để sử dụng truyền thống cho các vòng lặp. (Hãy xem xét điều đó i < leni++có thể được thực hiện bởi động cơ, chứ không phải bởi người phiên dịch.)
joeytwiddle

Câu trả lời:


7022

TL; DR

  • Đừng sử dụng for-intrừ khi bạn sử dụng nó với các biện pháp bảo vệ hoặc ít nhất là nhận thức được tại sao nó có thể cắn bạn.
  • Đặt cược tốt nhất của bạn thường là

    • một for-ofvòng lặp (chỉ ES2015 +),
    • Array#forEach( spec|MDN ) (hoặc họ hàng của nó somevà như vậy) (chỉ ES5 +),
    • kiểu cũ đơn giản for vòng lặp ,
    • hoặc for-invới các biện pháp bảo vệ.

Nhưng còn nhiều điều để khám phá, đọc tiếp ...


JavaScript có ngữ nghĩa mạnh mẽ để lặp qua các mảng và các đối tượng giống như mảng. Tôi đã chia câu trả lời thành hai phần: Tùy chọn cho mảng chính hãng và tùy chọn cho những thứ giống như mảng , chẳng hạn như argumentsđối tượng, các đối tượng lặp khác (ES2015 +), bộ sưu tập DOM, v.v.

Tôi sẽ nhanh chóng lưu ý rằng bạn có thể sử dụng các tùy chọn ES2015 ngay bây giờ , ngay cả trên các công cụ ES5, bằng cách chuyển mã ES2015 sang ES5. Tìm kiếm "dịch mã ES2015" / "Chuyển mã ES6" để biết thêm ...

Được rồi, hãy nhìn vào các lựa chọn của chúng tôi:

Đối với mảng thực tế

Bạn có ba tùy chọn trong ECMAScript 5 ("ES5"), phiên bản được hỗ trợ rộng rãi nhất vào lúc này và hai tùy chọn khác được thêm vào trong ECMAScript 2015 ("ES2015", "ES6"):

  1. Sử dụng forEachvà liên quan (ES5 +)
  2. Sử dụng một forvòng lặp đơn giản
  3. Sử dụng for-in đúng
  4. Sử dụng for-of(sử dụng một trình vòng lặp ngầm) (ES2015 +)
  5. Sử dụng một trình vòng lặp rõ ràng (ES2015 +)

Chi tiết:

1. Sử dụng forEachvà liên quan

Trong bất kỳ môi trường hiện đại mơ hồ nào (vì vậy, không phải IE8) nơi bạn có quyền truy cập vào các Arraytính năng được thêm bởi ES5 (trực tiếp hoặc sử dụng polyfill), bạn có thể sử dụng forEach( spec| MDN):

var a = ["a", "b", "c"];
a.forEach(function(entry) {
    console.log(entry);
});

forEachchấp nhận chức năng gọi lại và, tùy chọn, một giá trị sẽ sử dụng như thiskhi gọi cuộc gọi lại đó (không được sử dụng ở trên). Gọi lại được gọi cho mỗi mục trong mảng, theo thứ tự, bỏ qua các mục không tồn tại trong các mảng thưa thớt. Mặc dù tôi chỉ sử dụng một đối số ở trên, cuộc gọi lại được gọi với ba: Giá trị của mỗi mục nhập, chỉ mục của mục nhập đó và tham chiếu đến mảng bạn đang lặp lại (trong trường hợp chức năng của bạn không có sẵn ).

Trừ khi bạn hỗ trợ các trình duyệt lỗi thời như IE8 (mà NetApps chỉ chiếm hơn 4% thị phần vào thời điểm viết bài này vào tháng 9 năm 2016), bạn có thể vui vẻ sử dụng forEachtrong một trang web có mục đích chung mà không cần sử dụng . Nếu bạn cần hỗ trợ các trình duyệt lỗi thời, việc làm mờ / polyfilling forEachdễ dàng thực hiện (tìm kiếm "es5 shim" cho một số tùy chọn).

forEach có lợi ích là bạn không phải khai báo các biến chỉ mục và giá trị trong phạm vi chứa, vì chúng được cung cấp dưới dạng đối số cho hàm lặp và do đó chỉ nằm trong phạm vi lặp.

Nếu bạn lo lắng về chi phí thời gian chạy của việc thực hiện một cuộc gọi hàm cho mỗi mục nhập mảng, thì đừng; chi tiết .

Ngoài ra, forEachlà chức năng "lặp qua tất cả", nhưng ES5 đã định nghĩa một số chức năng "hữu ích khác theo cách của bạn thông qua mảng và thực hiện mọi thứ", bao gồm:

  • every(dừng lặp lại lần đầu tiên khi gọi lại trở lại falsehoặc một cái gì đó falsey)
  • some(dừng lặp lại lần đầu tiên khi gọi lại trở lại truehoặc một cái gì đó trung thực)
  • filter(tạo một mảng mới bao gồm các phần tử trong đó hàm bộ lọc trả về truevà bỏ qua các phần tử mà nó trả về false)
  • map (tạo một mảng mới từ các giá trị được trả về bởi hàm gọi lại)
  • reduce (xây dựng một giá trị bằng cách gọi lại cuộc gọi lại, chuyển các giá trị trước đó; xem thông số kỹ thuật để biết chi tiết; hữu ích cho việc tóm tắt nội dung của một mảng và nhiều thứ khác)
  • reduceRight(thích reduce, nhưng hoạt động theo thứ tự giảm dần chứ không phải tăng dần)

2. Sử dụng một forvòng lặp đơn giản

Đôi khi những cách cũ là tốt nhất:

var index;
var a = ["a", "b", "c"];
for (index = 0; index < a.length; ++index) {
    console.log(a[index]);
}

Nếu chiều dài của mảng sẽ không thay đổi trong vòng lặp, và đó là trong mã hiệu suất nhạy cảm (không), một phiên bản hơi phức tạp hơn grabbing chiều dài lên phía trước có thể là một nhỏ nhanh hơn chút:

var index, len;
var a = ["a", "b", "c"];
for (index = 0, len = a.length; index < len; ++index) {
    console.log(a[index]);
}

Và / hoặc đếm ngược:

var index;
var a = ["a", "b", "c"];
for (index = a.length - 1; index >= 0; --index) {
    console.log(a[index]);
}

Nhưng với các công cụ JavaScript hiện đại, hiếm khi bạn cần tìm ra chút nước ép cuối cùng.

Trong ES2015 trở lên, bạn có thể đặt biến chỉ số và giá trị cục bộ thành forvòng lặp:

let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
    let value = a[index];
    console.log(index, value);
}
//console.log(index);   // would cause "ReferenceError: index is not defined"
//console.log(value);   // would cause "ReferenceError: value is not defined"

Và khi bạn làm điều đó, không chỉ valuemà còn indexđược tạo lại cho mỗi lần lặp vòng lặp, nghĩa là các bao đóng được tạo trong thân vòng lặp giữ một tham chiếu đến index(và value) được tạo cho lần lặp cụ thể đó:

let divs = document.querySelectorAll("div");
for (let index = 0; index < divs.length; ++index) {
    divs[index].addEventListener('click', e => {
        console.log("Index is: " + index);
    });
}

Nếu bạn có năm div, bạn sẽ nhận được "Chỉ số là: 0" nếu bạn nhấp vào lần đầu tiên và "Chỉ mục là: 4" nếu bạn nhấp vào lần cuối. Điều này không hoạt động nếu bạn sử dụng varthay vì let.

3. Sử dụng for-in đúng

Bạn sẽ nhận được những người bảo bạn sử dụng for-in, nhưng đó không phải for-inlà để làm gì . for-incác vòng lặp thông qua các thuộc tính vô số của một đối tượng , không phải là các chỉ mục của một mảng. Đơn hàng không được đảm bảo , ngay cả trong ES2015 (ES6). ES2015 + không xác định một để tài sản đối tượng (thông qua [[OwnPropertyKeys]], [[Enumerate]]và những điều mà sử dụng chúng như thế Object.getOwnPropertyKeys), nhưng nó không xác định rằng for-insẽ đi theo thứ tự đó; ES2020 đã làm, mặc dù. (Chi tiết trong câu trả lời khác này .)

Các trường hợp sử dụng thực sự duy nhất cho for-inmột mảng là:

  • Đó là một mảng thưa thớt với những khoảng trống lớn trong đó, hoặc
  • Bạn đang sử dụng các thuộc tính không phải là phần tử và bạn muốn đưa chúng vào vòng lặp

Chỉ nhìn vào ví dụ đầu tiên đó: Bạn có thể sử dụng for-inđể truy cập các phần tử mảng thưa thớt đó nếu bạn sử dụng các biện pháp bảo vệ phù hợp:

// `a` is a sparse array
var key;
var a = [];
a[0] = "a";
a[10] = "b";
a[10000] = "c";
for (key in a) {
    if (a.hasOwnProperty(key)  &&        // These checks are
        /^0$|^[1-9]\d*$/.test(key) &&    // explained
        key <= 4294967294                // below
        ) {
        console.log(a[key]);
    }
}

Lưu ý ba kiểm tra:

  1. Rằng đối tượng có thuộc tính riêng của nó theo tên đó (không phải đối tượng mà nó thừa hưởng từ nguyên mẫu của nó) và

  2. Khóa đó là tất cả các chữ số thập phân (ví dụ: dạng chuỗi thông thường, không phải ký hiệu khoa học) và

  3. Giá trị của khóa khi bị ép buộc thành một số là <= 2 ^ 32 - 2 (là 4.294.967.294). Con số đó đến từ đâu? Đó là một phần định nghĩa của một chỉ mục mảng trong đặc tả . Các số khác (không phải số nguyên, số âm, số lớn hơn 2 ^ 32 - 2) không phải là chỉ mục mảng. Lý do là 2 ^ 32 - 2 là vì nó làm cho giá trị chỉ số lớn nhất thấp hơn 2 ^ 32 - 1 , đó là giá trị tối đa mà một mảng lengthcó thể có. (Ví dụ: độ dài của một mảng phù hợp với số nguyên không dấu 32 bit.) (Đạo cụ cho RobG để chỉ ra trong một nhận xét trên bài đăng trên blog của tôi rằng thử nghiệm trước đây của tôi không hoàn toàn đúng.)

Bạn sẽ không làm điều đó trong mã nội tuyến, tất nhiên. Bạn sẽ viết một hàm tiện ích. Có lẽ:

4. Sử dụng for-of(sử dụng một trình vòng lặp ngầm) (ES2015 +)

ES2015 đã thêm các trình vòng lặp vào JavaScript. Cách dễ nhất để sử dụng các trình vòng lặp là for-ofcâu lệnh mới . Nó trông như thế này:

const a = ["a", "b", "c"];
for (const val of a) {
    console.log(val);
}

Dưới vỏ bọc, điều đó nhận được một trình vòng lặp từ mảng và lặp qua nó, nhận các giá trị từ nó. Điều này không có vấn đề gì khi sử dụng for-in, bởi vì nó sử dụng một trình vòng lặp được xác định bởi đối tượng (mảng) và các mảng xác định rằng các trình vòng lặp của chúng lặp qua các mục nhập của chúng (không phải thuộc tính của chúng). Không giống như for-introng ES5, thứ tự mà các mục được truy cập là thứ tự số của các chỉ mục của chúng.

5. Sử dụng một trình vòng lặp rõ ràng (ES2015 +)

Đôi khi, bạn có thể muốn sử dụng một trình vòng lặp rõ ràng . Bạn cũng có thể làm điều đó, mặc dù nó còn cục mịch hơn nhiều for-of. Nó trông như thế này:

const a = ["a", "b", "c"];
const it = a.values();
let entry;
while (!(entry = it.next()).done) {
    console.log(entry.value);
}

Iterator là một đối tượng khớp với định nghĩa của Iterator trong đặc tả. nextPhương thức của nó trả về một đối tượng kết quả mới mỗi lần bạn gọi nó. Đối tượng kết quả có một thuộc tính, donecho chúng ta biết liệu nó đã được thực hiện chưa và một thuộc tính valuecó giá trị cho lần lặp đó. ( donelà tùy chọn nếu có false, valuelà tùy chọn nếu có undefined.)

Ý nghĩa của valuethay đổi tùy thuộc vào iterator; mảng hỗ trợ (ít nhất) ba hàm trả về các vòng lặp:

  • values(): Đây là cái tôi đã sử dụng ở trên. Nó trả về một iterator trong đó mỗi valuelà sự xâm nhập mảng cho lặp đó ( "a", "b""c"trong ví dụ trước đó).
  • keys(): Trả về một trình vòng lặp trong đó mỗi lần lặp valuelà chìa khóa cho lần lặp đó (vì vậy đối với phần atrên của chúng tôi , đó sẽ là "0", sau đó "1", sau đó "2").
  • entries(): Trả về một iterator trong đó mỗi valuelà một mảng trong biểu mẫu [key, value]cho lần lặp đó.

Đối với các đối tượng giống như mảng

Ngoài các mảng thực, còn có các đối tượng giống như mảnglengththuộc tính và thuộc tính với tên số: NodeListthể hiện, argumentsđối tượng, v.v ... Làm thế nào để chúng ta lặp qua nội dung của chúng?

Sử dụng bất kỳ tùy chọn nào ở trên cho mảng

Ít nhất một số, và có thể là hầu hết hoặc thậm chí là tất cả các cách tiếp cận mảng ở trên thường áp dụng tốt như nhau cho các đối tượng giống như mảng:

  1. Sử dụng forEachvà liên quan (ES5 +)

    Các hàm khác nhau trên Array.prototypelà "chung chung có chủ ý" và thường có thể được sử dụng trên các đối tượng giống như mảng thông qua Function#callhoặc Function#apply. (Xem Caveat cho các đối tượng được cung cấp bởi máy chủ ở cuối câu trả lời này, nhưng đó là một vấn đề hiếm gặp.)

    Giả sử bạn muốn sử dụng forEachtrên một Node's childNodestài sản. Bạn sẽ làm điều này:

    Array.prototype.forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });

    Nếu bạn sẽ làm điều đó rất nhiều, bạn có thể muốn lấy một bản sao của tham chiếu hàm vào một biến để sử dụng lại, ví dụ:

    // (This is all presumably in some scoping function)
    var forEach = Array.prototype.forEach;
    
    // Then later...
    forEach.call(node.childNodes, function(child) {
        // Do something with `child`
    });
  2. Sử dụng một forvòng lặp đơn giản

    Rõ ràng, một forvòng lặp đơn giản áp dụng cho các đối tượng giống như mảng.

  3. Sử dụng for-in đúng

    for-invới các biện pháp bảo vệ tương tự như với một mảng cũng sẽ hoạt động với các đối tượng giống như mảng; cảnh báo cho các đối tượng cung cấp máy chủ trên # 1 ở trên có thể được áp dụng.

  4. Sử dụng for-of(sử dụng một trình vòng lặp ngầm) (ES2015 +)

    for-ofsử dụng trình vòng lặp được cung cấp bởi đối tượng (nếu có). Điều đó bao gồm các đối tượng cung cấp máy chủ. Chẳng hạn, đặc tả cho NodeListtừ querySelectorAllđã được cập nhật để hỗ trợ phép lặp. Thông số kỹ thuật cho HTMLCollectiontừ getElementsByTagNamekhông.

  5. Sử dụng một trình vòng lặp rõ ràng (ES2015 +)

    Xem # 4.

Tạo một mảng thực sự

Đôi khi, bạn có thể muốn chuyển đổi một đối tượng giống như mảng thành một mảng thực sự. Làm điều đó thật dễ dàng đáng ngạc nhiên:

  1. Sử dụng slicephương pháp của mảng

    Chúng ta có thể sử dụng slicephương thức của mảng, giống như các phương thức khác được đề cập ở trên là "chung chung có chủ ý" và do đó có thể được sử dụng với các đối tượng giống như mảng, như sau:

    var trueArray = Array.prototype.slice.call(arrayLikeObject);

    Vì vậy, ví dụ, nếu chúng ta muốn chuyển đổi một NodeListmảng thành một mảng thực sự, chúng ta có thể làm điều này:

    var divs = Array.prototype.slice.call(document.querySelectorAll("div"));

    Xem Caveat cho các đối tượng cung cấp máy chủ dưới đây. Cụ thể, lưu ý rằng điều này sẽ thất bại trong IE8 trở về trước, điều này không cho phép bạn sử dụng các đối tượng do máy chủ cung cấp như thisthế.

  2. Sử dụng cú pháp lây lan ( ...)

    Cũng có thể sử dụng cú pháp lây lan của ES2015 với các công cụ JavaScript hỗ trợ tính năng này. Giống như for-of, điều này sử dụng trình vòng lặp được cung cấp bởi đối tượng (xem # 4 trong phần trước):

    var trueArray = [...iterableObject];

    Vì vậy, ví dụ, nếu chúng ta muốn chuyển đổi một NodeListmảng thành một mảng thực sự, với cú pháp trải rộng, điều này trở nên khá ngắn gọn:

    var divs = [...document.querySelectorAll("div")];
  3. Sử dụng Array.from

    Array.from (đặc tả) | (MDN) (ES2015 +, nhưng dễ dàng được điền đầy đủ) tạo một mảng từ một đối tượng giống như mảng, trước tiên tùy ý chuyển các mục qua chức năng ánh xạ. Vì thế:

    var divs = Array.from(document.querySelectorAll("div"));

    Hoặc nếu bạn muốn lấy một mảng tên thẻ của các thành phần với một lớp nhất định, bạn sẽ sử dụng chức năng ánh xạ:

    // Arrow function (ES2015):
    var divs = Array.from(document.querySelectorAll(".some-class"), element => element.tagName);
    
    // Standard function (since `Array.from` can be shimmed):
    var divs = Array.from(document.querySelectorAll(".some-class"), function(element) {
        return element.tagName;
    });

Hãy cẩn thận cho các đối tượng cung cấp máy chủ

Nếu bạn sử dụng các Array.prototypehàm với các đối tượng giống như mảng do máy chủ cung cấp (danh sách DOM và các thứ khác do trình duyệt cung cấp thay vì công cụ JavaScript), bạn cần chắc chắn kiểm tra trong môi trường đích của mình để đảm bảo đối tượng được cung cấp máy chủ hoạt động đúng . Hầu hết đều cư xử đúng mực (bây giờ), nhưng điều quan trọng là phải kiểm tra. Lý do là hầu hết các Array.prototypephương thức bạn có thể muốn sử dụng đều dựa vào đối tượng do máy chủ cung cấp để trả lời trung thực cho [[HasProperty]]hoạt động trừu tượng . Khi viết bài này, các trình duyệt thực hiện rất tốt điều này, nhưng thông số 5.1 đã cho phép khả năng một đối tượng cung cấp máy chủ có thể không trung thực. Đó là trong §8.6.2 , một số đoạn bên dưới bảng lớn gần đầu phần đó), trong đó có đoạn:

Các đối tượng máy chủ có thể thực hiện các phương thức nội bộ này theo bất kỳ cách nào trừ khi được quy định khác; ví dụ, một khả năng là [[Get]][[Put]]đối với một đối tượng máy chủ cụ thể thực sự tìm nạp và lưu trữ các giá trị thuộc tính nhưng [[HasProperty]]luôn tạo ra sai .

(Tôi không thể tìm thấy sự nói dài giòng tương đương trong ES2015 spec, nhưng đó là ràng buộc để vẫn là trường hợp.) Một lần nữa, như vậy văn bản phổ biến host-cung cấp mảng giống như các đối tượng trong trình duyệt hiện đại [ NodeListtrường hợp, ví dụ] làm tay cầm [[HasProperty]]chính xác, nhưng điều quan trọng là phải kiểm tra.)


44
Tôi cũng muốn thêm rằng .forEachkhông thể bị phá vỡ hiệu quả. Bạn phải ném một ngoại lệ để thực hiện phá vỡ.
Pijusn

82
@Pius: Nếu bạn muốn phá vỡ vòng lặp, bạn có thể sử dụng some. (Tôi cũng muốn cho phép phá vỡ forEach, nhưng họ, ừm, đã không hỏi tôi. ;-))
TJ Crowder

6
@TJCrowder Đúng, mặc dù nó trông giống như một công việc xung quanh vì nó không phải là mục đích chính của nó.
Pijusn

8
@ user889030: Bạn cần ,sau k=0, không phải a ;. Hãy nhớ rằng, lập trình là nhiều thứ, một trong số đó rất chú ý đến chi tiết ... :-)
TJ Crowder

5
@JimB: Điều đó được đề cập ở trên (và lengthkhông phải là một phương pháp). :-)
TJ Crowder

513

Lưu ý : Câu trả lời này là vô vọng lỗi thời. Đối với một cách tiếp cận hiện đại hơn, hãy xem các phương thức có sẵn trên một mảng . Phương pháp quan tâm có thể là:

  • cho mỗi
  • bản đồ
  • bộ lọc
  • khóa kéo
  • giảm
  • mỗi
  • một số

Cách tiêu chuẩn để lặp lại một mảng trong JavaScript là vanilla for-loop:

var length = arr.length,
    element = null;
for (var i = 0; i < length; i++) {
  element = arr[i];
  // Do something with element
}

Tuy nhiên, lưu ý rằng phương pháp này chỉ tốt nếu bạn có một mảng dày đặc và mỗi chỉ mục bị chiếm bởi một phần tử. Nếu mảng thưa thớt, thì bạn có thể gặp vấn đề về hiệu năng với cách tiếp cận này, vì bạn sẽ lặp lại rất nhiều chỉ số không thực sự tồn tại trong mảng. Trong trường hợp này, một for .. in-loop có thể là một ý tưởng tốt hơn. Tuy nhiên , bạn phải sử dụng các biện pháp bảo vệ thích hợp để đảm bảo rằng chỉ các thuộc tính mong muốn của mảng (nghĩa là các thành phần mảng) được hành động, vì for..in-loop cũng sẽ được liệt kê trong các trình duyệt cũ hoặc nếu các thuộc tính bổ sung được xác định là enumerable.

Trong ECMAScript 5 sẽ có một phương thức forEach trên nguyên mẫu mảng, nhưng nó không được hỗ trợ trong các trình duyệt cũ. Vì vậy, để có thể sử dụng nó một cách nhất quán, bạn phải có một môi trường hỗ trợ nó (ví dụ: Node.js cho JavaScript phía máy chủ) hoặc sử dụng "Polyfill". Tuy nhiên, Polyfill cho chức năng này là tầm thường và vì nó làm cho mã dễ đọc hơn, nó là một polyfill tốt để bao gồm.


2
Tại sao for(instance in objArray) không sử dụng đúng? Nó trông đơn giản hơn đối với tôi, nhưng tôi nghe bạn nói về nó như là một cách không chính xác để sử dụng?
Dante1986

21
Bạn có thể sử dụng bộ nhớ đệm chiều dài nội tuyến: for (var i = 0, l = Array.length; i <l; i ++)
Robert Hoffmann

3
Là dấu phẩy ở cuối dòng đầu tiên có chủ ý, hay nó là một lỗi đánh máy (có thể là dấu chấm phẩy)?
Mohd Abdul Mujib

6
@ Wardha-Web Đó là cố ý. Nó cho phép chúng ta khai báo nhiều biến bằng một từ khóa var. Nếu chúng tôi đã sử dụng một dấu chấm phẩy, thì elementnó sẽ được khai báo trong phạm vi toàn cầu (hay nói đúng hơn là, JSHint sẽ hét vào mặt chúng tôi trước khi nó được sản xuất).
PatrikAkerstrand

239

Nếu bạn đang sử dụng thư viện jQuery , bạn có thể sử dụng jQuery.each :

$.each(yourArray, function(index, value) {
  // do your stuff here
});

BIÊN TẬP :

Theo câu hỏi, người dùng muốn mã trong javascript thay vì jquery để chỉnh sửa

var length = yourArray.length;   
for (var i = 0; i < length; i++) {
  // Do something with yourArray[i].
}

2
Tôi có lẽ sẽ sử dụng câu trả lời này thường xuyên nhất. Nó không phải là câu trả lời tốt nhất cho câu hỏi, nhưng trong thực tế sẽ là cách đơn giản nhất và có thể áp dụng nhất cho những người trong chúng ta sử dụng jQuery. Tôi nghĩ rằng tất cả chúng ta nên học cách vanilla mặc dù. Nó không bao giờ đau để mở rộng sự hiểu biết của bạn.
mopsyd

47
Chỉ vì lợi ích của nó: jQuery từng chậm hơn nhiều so với các giải pháp gốc. JQuery khuyên bạn nên sử dụng JavaScript nguyên gốc thay vì jQuery khi có thể. jsperf.com/browser-diet-jquery-each-vs-for-loop
Kevin Boss

8
Không sử dụng jQuery khi bạn có thể sử dụng vanilla js
Noe

2
Bám sát tiêu chuẩn JS, giữ cho các thư viện bên thứ 3 không có câu trả lời trừ khi không có giải pháp ngôn ngữ bản địa
scubasteve

116

Vòng lặp ngược

Tôi nghĩ rằng đảo ngược cho vòng lặp xứng đáng được đề cập ở đây:

for (var i = array.length; i--; ) {
     // process array[i]
}

Ưu điểm:

  • Bạn không cần phải khai báo một lenbiến tạm thời hoặc so sánh với array.lengthmỗi lần lặp, một trong số đó có thể là tối ưu hóa phút.
  • Loại bỏ anh chị em khỏi DOM theo thứ tự ngược thường hiệu quả hơn . (Trình duyệt cần thực hiện ít dịch chuyển các phần tử trong mảng bên trong của nó.)
  • Nếu bạn sửa đổi mảng trong khi lặp, tại hoặc sau chỉ mục i (ví dụ: bạn xóa hoặc chèn một mục tại array[i]), thì vòng lặp chuyển tiếp sẽ bỏ qua mục được chuyển sang trái vào vị trí i hoặc xử lý lại mục thứ i chuyển sang phải. Trong vòng lặp for truyền thống, bạn có thể cập nhật i để trỏ đến mục tiếp theo cần xử lý - 1, nhưng chỉ cần đảo ngược hướng lặp thường đơn giản hơn giải phápthanh lịch hơn .
  • Tương tự, khi sửa đổi hoặc loại bỏ các phần tử DOM lồng nhau , xử lý ngược lại có thể tránh được các lỗi . Ví dụ, hãy xem xét sửa đổi bên trongHTML của nút cha trước khi xử lý các nút con của nó. Vào thời điểm nút con đạt được, nó sẽ được tách ra khỏi DOM, đã được thay thế bởi một đứa trẻ mới được tạo khi nội bộHTML của cha mẹ được viết.
  • ngắn hơn để gõ và đọc , so với một số tùy chọn khác có sẵn. Mặc dù nó thua forEach()và trước ES6for ... of .

Nhược điểm:

  • Nó xử lý các mục theo thứ tự ngược lại. Nếu bạn đang xây dựng một mảng mới từ kết quả hoặc in mọi thứ trên màn hình, một cách tự nhiên đầu ra sẽ bị đảo ngược so với thứ tự ban đầu.
  • Liên tục chèn anh chị em vào DOM như một đứa trẻ đầu tiên để giữ trật tự của họ là ít hiệu quả hơn . (Trình duyệt sẽ phải thay đổi mọi thứ ngay.) Để tạo các nút DOM hiệu quả và theo thứ tự, chỉ cần lặp về phía trước và nối thêm như bình thường (và cũng sử dụng "đoạn tài liệu").
  • Vòng lặp ngược gây nhầm lẫn cho các nhà phát triển cơ sở. (Bạn có thể coi đó là một lợi thế, tùy thuộc vào triển vọng của bạn.)

Tôi có nên luôn luôn sử dụng nó?

Một số nhà phát triển sử dụng vòng lặp đảo ngược theo mặc định , trừ khi có lý do chính đáng để lặp lại.

Mặc dù mức tăng hiệu suất thường không đáng kể, nhưng nó hét lên:

"Chỉ cần làm điều này cho mọi mục trong danh sách, tôi không quan tâm đến thứ tự!"

Tuy nhiên trong thực tế đó là không thực sự là một dấu hiệu đáng tin cậy về ý định, vì nó là không thể phân biệt từ những dịp khi bạn làm việc chăm sóc về thứ tự, và thực sự làm cần thiết để lặp ngược lại. Vì vậy, trên thực tế, một cấu trúc khác sẽ là cần thiết để thể hiện chính xác ý định "không quan tâm", một cái gì đó hiện không có sẵn trong hầu hết các ngôn ngữ, bao gồm ECMAScript, nhưng có thể được gọi là, ví dụ , forEachUnordered().

Nếu thứ tự không thành vấn đề và hiệu quả là một mối quan tâm (trong vòng lặp trong cùng của trò chơi hoặc công cụ hoạt hình), thì có thể chấp nhận sử dụng vòng lặp ngược cho mô hình đi tới của bạn. Chỉ cần nhớ rằng nhìn thấy một vòng lặp ngược trong mã hiện tại không nhất thiết có nghĩa là thứ tự không liên quan!

Tốt hơn là sử dụng forEach ()

Nói chung đối với mã cấp cao hơn trong đó sự rõ ràng và an toàn là mối quan tâm lớn hơn, trước đây tôi đã khuyến nghị sử dụng Array::forEachlàm mẫu mặc định của bạn để lặp (mặc dù những ngày này tôi thích sử dụng hơn for..of). Các lý do để thích forEachvòng lặp ngược là:

  • Nó là rõ ràng hơn để đọc.
  • Nó chỉ ra rằng tôi sẽ không bị thay đổi trong khối (điều này luôn luôn là một bất ngờ có thể ẩn giấu trong thời gian dài forwhile các vòng lặp ).
  • Nó cung cấp cho bạn một phạm vi miễn phí cho việc đóng cửa.
  • Nó làm giảm rò rỉ các biến cục bộ và va chạm ngẫu nhiên với (và đột biến) các biến ngoài.

Sau đó, khi bạn thấy vòng lặp đảo ngược trong mã của mình, đó là một gợi ý rằng nó bị đảo ngược vì một lý do chính đáng (có lẽ là một trong những lý do được mô tả ở trên). Và nhìn thấy một vòng lặp truyền thống cho vòng lặp có thể chỉ ra rằng sự dịch chuyển có thể diễn ra.

(Nếu cuộc thảo luận về ý định không có ý nghĩa với bạn, thì bạn và mã của bạn có thể được hưởng lợi từ việc xem bài giảng của Crockford về Phong cách lập trình & Bộ não của bạn .)

Bây giờ thậm chí còn tốt hơn để sử dụng cho..of!

Có một cuộc tranh luận về việc có for..ofhay forEach()không thích hợp hơn:

  • Để hỗ trợ trình duyệt tối đa, for..of yêu cầu một polyfill cho các trình vòng lặp, làm cho ứng dụng của bạn chậm hơn một chút để thực thi và lớn hơn một chút để tải xuống.

  • Vì lý do đó (và để khuyến khích sử dụng mapfilter), một số hướng dẫn kiểu đầu cuối cấm for..ofhoàn toàn!

  • Nhưng những lo ngại trên không thể áp dụng cho các ứng dụng Node.js, for..ofhiện được hỗ trợ tốt.

  • Và hơn nữa await không hoạt động bên trong forEach(). Sử dụng for..ofmô hình rõ ràng nhất trong trường hợp này.

Cá nhân, tôi có xu hướng sử dụng bất cứ thứ gì có vẻ dễ đọc nhất, trừ khi hiệu suất hoặc thu nhỏ đã trở thành mối quan tâm chính. Vì vậy, những ngày này tôi thích sử dụng for..ofthay vì forEach(), nhưng tôi sẽ luôn luôn sử dụng maphoặc filterhoặc findhoặc somekhi áp dụng. (Vì lợi ích của đồng nghiệp, tôi hiếm khi sử dụngreduce .)


Làm thế nào nó hoạt động?

for (var i = 0; i < array.length; i++) { ... }   // Forwards

for (var i = array.length; i--; )    { ... }   // Reverse

Bạn sẽ nhận thấy đó i--là mệnh đề giữa (nơi chúng ta thường thấy một so sánh) và mệnh đề cuối là trống (nơi chúng ta thường thấy i++). Điều đó có nghĩa i--là cũng được sử dụng như là điều kiện để tiếp tục. Điều quan trọng, nó được thực thi và kiểm tra trước mỗi lần lặp.

  • Làm thế nào nó có thể bắt đầu array.lengthmà không phát nổ?

    i--chạy trước mỗi lần lặp, nên trong lần lặp đầu tiên, chúng tôi thực sự sẽ truy cập vào mục array.length - 1mà tránh mọi vấn đề với Array-out-of undefined các mục .

  • Tại sao nó không dừng lặp lại trước chỉ số 0?

    Vòng lặp sẽ dừng lặp khi điều kiện i-- ước tính thành giá trị falsey (khi nó mang lại 0).

    Thủ thuật là không giống như --i, i--toán tử trailing giảm inhưng mang lại giá trị trước khi giảm. Bảng điều khiển của bạn có thể chứng minh điều này:

    > var i = 5; [i, i--, i];

    [5, 5, 4]

    Vì vậy, trong lần lặp lại cuối cùng, tôi trước đó là 1i--biểu thức thay đổi thành 0 nhưng thực tế mang lại 1 (trung thực), và do đó điều kiện vượt qua. Ở lần lặp tiếp theo i--thay đổi i thành -1 nhưng mang lại 0 (falsey), khiến việc thực thi ngay lập tức rơi ra khỏi đáy của vòng lặp.

    Trong vòng lặp truyền thống cho vòng lặp, i++++icó thể thay thế cho nhau (như Douglas Crockford chỉ ra). Tuy nhiên, trong vòng lặp ngược lại, vì phần giảm của chúng tôi cũng là biểu thức điều kiện của chúng tôi, chúng tôi phải gắn bó i--nếu muốn xử lý mục ở chỉ số 0.


Câu đố

Một số người thích vẽ một mũi tên nhỏ trong forvòng lặp ngược và kết thúc bằng một cái nháy mắt:

for (var i = array.length; i --> 0 ;) {

Tín dụng đến WYL để cho tôi thấy những lợi ích và sự khủng khiếp của vòng lặp ngược.


3
Tôi quên để thêm điểm chuẩn . Tôi cũng quên đề cập đến cách lặp ngược là một tối ưu hóa đáng kể trên các bộ xử lý 8 bit như 6502, nơi bạn thực sự có được sự so sánh miễn phí!
joeytwiddle

Câu trả lời tương tự được đưa ra chính xác hơn nhiều ở đây (về một câu hỏi khác).
joeytwiddle

84

Một số ngôn ngữ kiểu C sử dụng foreachđể lặp qua các bảng liệt kê. Trong JavaScript, điều này được thực hiện với for..incấu trúc vòng lặp :

var index,
    value;
for (index in obj) {
    value = obj[index];
}

Có một cái bẫy. for..insẽ lặp qua từng thành viên có thể đếm được của từng đối tượng và các thành viên trên nguyên mẫu của nó. Để tránh đọc các giá trị được kế thừa thông qua nguyên mẫu của đối tượng, chỉ cần kiểm tra xem thuộc tính có thuộc về đối tượng không:

for (i in obj) {
    if (obj.hasOwnProperty(i)) {
        //do stuff
    }
}

Ngoài ra, ECMAScript 5 đã thêm một forEachphương thức Array.prototypecó thể được sử dụng để liệt kê một mảng bằng cách sử dụng một calback (polyfill có trong các tài liệu để bạn vẫn có thể sử dụng nó cho các trình duyệt cũ hơn):

arr.forEach(function (val, index, theArray) {
    //do stuff
});

Điều quan trọng cần lưu ý là Array.prototype.forEachkhông bị hỏng khi gọi lại false. jQueryUnderscore.js cung cấp các biến thể của riêng họ eachđể cung cấp các vòng lặp có thể được ngắn mạch.


3
Vậy làm thế nào để một người thoát ra khỏi vòng lặp foreach ECMAScript5 như chúng ta sẽ làm cho một vòng lặp for bình thường hoặc vòng lặp foreach như được tìm thấy trong các ngôn ngữ kiểu C?
Ciaran Gallagher

5
@CiaranG, trong JavaScript, người ta thường thấy eachcác phương thức cho phép return falseđược sử dụng để thoát ra khỏi vòng lặp, nhưng với forEachđiều này không phải là một tùy chọn. Một cờ bên ngoài có thể được sử dụng (nghĩa là if (flag) return;, nhưng nó sẽ chỉ ngăn phần còn lại của cơ thể chức năng thực thi, forEachvẫn sẽ tiếp tục lặp lại trên toàn bộ bộ sưu tập.
zzzzBov

43

Nếu bạn muốn lặp qua một mảng, hãy sử dụng forvòng lặp ba phần tiêu chuẩn .

for (var i = 0; i < myArray.length; i++) {
    var arrayItem = myArray[i];
}

Bạn có thể nhận được một số tối ưu hóa hiệu suất bằng cách lưu trữ myArray.lengthhoặc lặp lại qua nó.


6
for (var i = 0, length = myArray.length; i <length; i ++) nên làm điều đó
Edson Medina

5
@EdsonMedina Điều đó cũng sẽ tạo ra một biến toàn cầu mới được gọi là length. ;)
joeytwiddle

4
@joeytwiddle Có, nhưng điều đó vượt quá phạm vi của bài đăng này. Bạn sẽ tạo ra một biến toàn cầu i nào.
Edson Medina

6
@EdsonMedina Xin lỗi, tôi đã hoàn toàn sai. Sử dụng ,sau khi chuyển nhượng không giới thiệu toàn cầu mới, vì vậy đề xuất của bạn là tốt ! Tôi đã nhầm lẫn điều này cho một vấn đề khác: Sử dụng =sau khi chuyển nhượng sẽ tạo ra một toàn cầu mới.
joeytwiddle

Coi chừng biến i không cục bộ vào vòng lặp. JavaScript không có phạm vi khối. Có lẽ tốt hơn là khai báo var i, length, arrayItem;trước vòng lặp để tránh sự hiểu lầm này.
James

35

Tôi biết đây là một bài viết cũ, và đã có rất nhiều câu trả lời tuyệt vời rồi. Để hoàn thiện hơn một chút, tôi nghĩ rằng tôi sẽ ném vào một cái khác bằng AngularJS . Tất nhiên, điều này chỉ áp dụng nếu bạn đang sử dụng Angular, rõ ràng, dù sao tôi cũng muốn đặt nó.

angular.forEachmất 2 đối số và một đối số thứ ba tùy chọn. Đối số đầu tiên là đối tượng (mảng) để lặp lại, đối số thứ hai là hàm lặp và đối số thứ ba tùy chọn là bối cảnh đối tượng (về cơ bản được gọi bên trong vòng lặp là 'this'.

Có nhiều cách khác nhau để sử dụng vòng lặp forEach của góc. Đơn giản nhất và có lẽ được sử dụng nhiều nhất là

var temp = [1, 2, 3];
angular.forEach(temp, function(item) {
    //item will be each element in the array
    //do something
});

Một cách khác hữu ích để sao chép các mục từ mảng này sang mảng khác là

var temp = [1, 2, 3];
var temp2 = [];
angular.forEach(temp, function(item) {
    this.push(item); //"this" refers to the array passed into the optional third parameter so, in this case, temp2.
}, temp2);

Mặc dù, bạn không phải làm điều đó, bạn chỉ cần làm như sau và nó tương đương với ví dụ trước:

angular.forEach(temp, function(item) {
    temp2.push(item);
});

Bây giờ có những ưu và nhược điểm của việc sử dụng angular.forEachchức năng trái ngược với forvòng lặp có hương vị vani được xây dựng .

Ưu

  • Dễ đọc
  • Dễ viết
  • Nếu có sẵn, angular.forEachsẽ sử dụng vòng lặp ES5 forEach. Bây giờ, tôi sẽ nhận được để efficientcy trong phần khuyết điểm, như các vòng foreach là nhiều chậm hơn so với cho vòng lặp. Tôi đề cập đến điều này như một chuyên gia vì thật tuyệt khi được thống nhất và chuẩn hóa.

Hãy xem xét 2 vòng lặp lồng nhau sau đây, chúng thực hiện chính xác cùng một điều. Giả sử chúng ta có 2 mảng đối tượng và mỗi đối tượng chứa một mảng kết quả, mỗi đối tượng có thuộc tính Giá trị là một chuỗi (hoặc bất cứ thứ gì). Và giả sử chúng ta cần lặp lại qua từng kết quả và nếu chúng bằng nhau thì thực hiện một số hành động:

angular.forEach(obj1.results, function(result1) {
    angular.forEach(obj2.results, function(result2) {
        if (result1.Value === result2.Value) {
            //do something
        }
    });
});

//exact same with a for loop
for (var i = 0; i < obj1.results.length; i++) {
    for (var j = 0; j < obj2.results.length; j++) {
        if (obj1.results[i].Value === obj2.results[j].Value) {
            //do something
        }
    }
}

Được cho đây là một ví dụ giả thuyết rất đơn giản, nhưng tôi đã viết ba lần nhúng cho các vòng lặp bằng cách sử dụng cách tiếp cận thứ hai và nó rất khó đọc và viết cho vấn đề đó.

Nhược điểm

  • Hiệu quả. angular.forEachvà bản địa forEach, đối với vấn đề đó, cả hai đều chậm hơn rất nhiều so với forvòng lặp thông thường .... chậm hơn khoảng 90% . Vì vậy, đối với các tập dữ liệu lớn, tốt nhất để bám vào forvòng lặp gốc .
  • Không nghỉ, tiếp tục, hoặc trả lại hỗ trợ. continuethực sự được hỗ trợ bởi " tai nạn ", để tiếp tục trong một angular.forEachđơn giản, bạn đặt một return;câu lệnh vào hàm giống như angular.forEach(array, function(item) { if (someConditionIsTrue) return; });nó sẽ khiến nó tiếp tục ra khỏi hàm cho phép lặp đó. Điều này cũng là do thực tế là người bản địa forEachkhông hỗ trợ phá vỡ hoặc tiếp tục.

Tôi chắc chắn cũng có nhiều ưu và nhược điểm khác, và xin vui lòng thêm bất kỳ điều gì bạn thấy phù hợp. Tôi cảm thấy rằng, điểm mấu chốt, nếu bạn cần hiệu quả, hãy gắn bó với forvòng lặp riêng cho nhu cầu lặp của bạn. Nhưng, nếu bộ dữ liệu của bạn nhỏ hơn và một số hiệu quả vẫn ổn để từ bỏ để đổi lấy khả năng đọc và ghi, thì bằng mọi cách, hãy ném angular.forEachvào một cậu bé hư hỏng đó.


34

Nếu bạn không nhớ làm trống mảng:

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

xsẽ chứa giá trị cuối cùng yvà nó sẽ bị xóa khỏi mảng. Bạn cũng có thể sử dụng shift()sẽ cung cấp và loại bỏ các mục đầu tiên từ y.


4
Nó không hoạt động nếu bạn có một mảng thưa thớt như thế [1, 2, undefined, 3].
M. Grzywaczewski

2
... hoặc thực sự bất cứ điều gì falsey: [1, 2, 0, 3]hoặc[true, true, false, true]
joeytwiddle

31

Một triển khai forEach ( xem trong jsFiddle ):

function forEach(list,callback) {
  var length = list.length;
  for (var n = 0; n < length; n++) {
    callback.call(list[n]);
  }
}

var myArray = ['hello','world'];

forEach(
  myArray,
  function(){
    alert(this); // do something
  }
);

2
iterator trong này, đang thực hiện một phép tính độ dài không cần thiết. Trong trường hợp lý tưởng, độ dài danh sách chỉ nên được tính một lần.
MIdhun Krishna

2
@MIdhunKrishna Tôi đã cập nhật câu trả lời của tôi và jsFiddle nhưng lưu ý rằng nó không đơn giản như bạn nghĩ. Kiểm tra câu hỏi
nmoliveira

2
Việc triển khai đầy đủ và đúng có thể được tìm thấy ở đây: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Kẻ

29

Có lẽ for(i = 0; i < array.length; i++)vòng lặp không phải là sự lựa chọn tốt nhất. Tại sao? Nếu bạn có điều này:

var array = new Array();
array[1] = "Hello";
array[7] = "World";
array[11] = "!";

Phương thức sẽ gọi từ array[0]đến array[2]. Đầu tiên, điều này trước tiên sẽ tham chiếu các biến bạn thậm chí không có, thứ hai bạn sẽ không có các biến trong mảng và thứ ba điều này sẽ làm cho mã mạnh hơn. Nhìn vào đây, đó là những gì tôi sử dụng:

for(var i in array){
    var el = array[i];
    //If you want 'i' to be INT just put parseInt(i)
    //Do something with el
}

Và nếu bạn muốn nó là một hàm, bạn có thể làm điều này:

function foreach(array, call){
    for(var i in array){
        call(array[i]);
    }
}

Nếu bạn muốn phá vỡ, logic hơn một chút:

function foreach(array, call){
    for(var i in array){
        if(call(array[i]) == false){
            break;
        }
    }
}

Thí dụ:

foreach(array, function(el){
    if(el != "!"){
        console.log(el);
    } else {
        console.log(el+"!!");
    }
});

Nó trở lại:

//Hello
//World
//!!!

29

Có ba triển khai foreachtrong jQuery như sau.

var a = [3,2];

$(a).each(function(){console.log(this.valueOf())}); //Method 1
$.each(a, function(){console.log(this.valueOf())}); //Method 2
$.each($(a), function(){console.log(this.valueOf())}); //Method 3

2
Nhật ký giao diện điều khiển chỉ dành cho bản demo. Đó là làm cho nó trở thành một ví dụ chạy cmplete.
Rajesh Paul

29

Kể từ ECMAScript 6:

list = [0, 1, 2, 3]
for (let obj of list) {
    console.log(obj)
}

Nơi oftránh những điều kỳ quặc liên quan invà làm cho nó hoạt động giống như forvòng lặp của bất kỳ ngôn ngữ nào khác, vàlet liên kết itrong vòng lặp trái ngược với chức năng.

Các dấu ngoặc ( {}) có thể được bỏ qua khi chỉ có một lệnh (ví dụ trong ví dụ trên).


28

Một giải pháp dễ dàng bây giờ sẽ là sử dụng thư viện underscore.js . Nó cung cấp nhiều công cụ hữu ích, chẳng hạn như eachvà sẽ tự động ủy thác công việc cho người bản xứ forEachnếu có.

Một ví dụ về CodePen về cách thức hoạt động của nó là:

var arr = ["elemA", "elemB", "elemC"];
_.each(arr, function(elem, index, ar)
{
...
});

Xem thêm


23

Không có bất kỳ for eachvòng lặp nào trong JavaScript gốc . Bạn có thể sử dụng các thư viện để có được chức năng này (tôi khuyên dùng Underscore.js ), sử dụng một forvòng lặp đơn giản .

for (var instance in objects) {
   ...
}

Tuy nhiên, lưu ý rằng có thể có lý do để sử dụng một thậm chí đơn giản hơn forvòng lặp (xem Stack Overflow câu hỏi Tại sao sử dụng “cho ... trong” với mảng lặp một ý tưởng tồi như vậy? )

var instance;
for (var i=0; i < objects.length; i++) {
    var instance = objects[i];
    ...
}

22

Đây là một trình lặp cho danh sách thưa thớt trong đó chỉ mục bắt đầu từ 0, đây là kịch bản điển hình khi xử lý tài liệu.getElementsByTagName hoặc document.querySelector ALL)

function each( fn, data ) {

    if(typeof fn == 'string')
        eval('fn = function(data, i){' + fn + '}');

    for(var i=0, L=this.length; i < L; i++) 
        fn.call( this[i], data, i );   

    return this;
}

Array.prototype.each = each;  

Ví dụ về việc sử dụng:

Ví dụ 1

var arr = [];
[1, 2, 3].each( function(a){ a.push( this * this}, arr);
arr = [1, 4, 9]

Ví dụ # 2

each.call(document.getElementsByTagName('p'), "this.className = data;",'blue');

Mỗi thẻ p được class="blue"

Ví dụ # 3

each.call(document.getElementsByTagName('p'), 
    "if( i % 2 == 0) this.className = data;",
    'red'
);

Mỗi thẻ p khác được class="red">

Ví dụ số 4

each.call(document.querySelectorAll('p.blue'), 
    function(newClass, i) {
        if( i < 20 )
            this.className = newClass;
    }, 'green'
);

Và cuối cùng, 20 thẻ p màu xanh đầu tiên được đổi thành màu xanh lá cây

Thận trọng khi sử dụng chuỗi làm hàm: hàm được tạo ngoài ngữ cảnh và chỉ được sử dụng khi bạn chắc chắn có phạm vi biến. Mặt khác, tốt hơn để vượt qua các chức năng trong đó phạm vi trực quan hơn.


21

một vài cách để lặp qua một mảng trong JavaScript, như sau:

cho - đó là một phổ biến nhất . Khối mã đầy đủ để lặp

var languages = ["Java", "JavaScript", "C#", "Python"];
var i, len, text;
for (i = 0, len = languages.length, text = ""; i < len; i++) {
    text += languages[i] + "<br>";
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

while - loop trong khi một điều kiện được thông qua. Nó dường như là vòng lặp nhanh nhất

var text = "";
var i = 0;
while (i < 10) {
    text +=  i + ") something<br>";
    i++;
}
document.getElementById("example").innerHTML = text;
<p id="example"></p>

do / while - cũng lặp qua một khối mã trong khi điều kiện là đúng, sẽ chạy ít nhất một lần

var text = ""
var i = 0;

do {
    text += i + ") something <br>";
    i++;
}
while (i < 10);

document.getElementById("example").innerHTML = text;
<p id="example"></p>

Vòng chức năng - forEach, map, filter, cũng reduce(loop họ thông qua chức năng, nhưng chúng được sử dụng nếu bạn cần phải làm điều gì đó với mảng của bạn, vv

// For example, in this case we loop through the number and double them up using the map function
var numbers = [65, 44, 12, 4];
document.getElementById("example").innerHTML = numbers.map(function(num){return num * 2});
<p id="example"></p>

Để biết thêm thông tin và ví dụ về lập trình chức năng trên mảng, hãy xem bài đăng trên blog Lập trình chức năng trong JavaScript: ánh xạ, bộ lọc và rút gọn .


Hiệu chỉnh nhỏ: forEachkhông phải là một vòng lặp "chức năng" vì nó không trả về một cái mới Array(thực ra nó không trả về bất cứ thứ gì), nó chỉ lặp đi lặp lại.
nicholaswmin

19

ECMAScript 5 (phiên bản trên JavaScript) để hoạt động với Mảng:

forEach - Lặp lại qua từng mục trong mảng và làm bất cứ điều gì bạn cần với mỗi mục.

['C', 'D', 'E'].forEach(function(element, index) {
  console.log(element + " is #" + (index+1) + " in the musical scale");
});

// Output
// C is the #1 in musical scale
// D is the #2 in musical scale
// E is the #3 in musical scale

Trong trường hợp, quan tâm nhiều hơn đến hoạt động trên mảng bằng cách sử dụng một số tính năng sẵn có.

map - Nó tạo ra một mảng mới với kết quả của hàm gọi lại. Phương pháp này là tốt để được sử dụng khi bạn cần định dạng các thành phần của mảng.

// Let's upper case the items in the array
['bob', 'joe', 'jen'].map(function(elem) {
  return elem.toUpperCase();
});

// Output: ['BOB', 'JOE', 'JEN']

giảm - Như tên đã nói, nó giảm mảng thành một giá trị bằng cách gọi hàm đã cho đi qua phần tử hiện tại và kết quả của lần thực hiện trước đó.

[1,2,3,4].reduce(function(previous, current) {
  return previous + current;
});
// Output: 10
// 1st iteration: previous=1, current=2 => result=3
// 2nd iteration: previous=3, current=3 => result=6
// 3rd iteration: previous=6, current=4 => result=10

every - Trả về true hoặc false nếu tất cả các phần tử trong mảng vượt qua kiểm tra trong hàm gọi lại.

// Check if everybody has 18 years old of more.
var ages = [30, 43, 18, 5];
ages.every(function(elem) {
  return elem >= 18;
});

// Output: false

bộ lọc - Rất giống với mọi bộ lọc ngoại trừ bộ lọc đó trả về một mảng với các phần tử trả về đúng cho hàm đã cho.

// Finding the even numbers
[1,2,3,4,5,6].filter(function(elem){
  return (elem % 2 == 0)
});

// Output: [2,4,6]

16

Không có khả năng sẵn sàng để đột nhập forEach. Để ngắt thực hiện, sử dụng Array#somenhư dưới đây:

[1,2,3].some(function(number) {
    return number === 1;
});

Điều này hoạt động bởi vì sometrả về true ngay khi bất kỳ cuộc gọi lại nào, được thực hiện theo thứ tự mảng, trả về true, ngắn mạch khi thực hiện phần còn lại. Câu trả lời gốc xem nguyên mẫu Array cho một số


13

Tôi cũng muốn thêm nó như là một thành phần của một vòng lặp ngược và một câu trả lời ở trên cho một người cũng thích cú pháp này.

var foo = [object,object,object];
for (var i = foo.length, item; item = foo[--i];) {
    console.log(item);
}

Ưu điểm:

Lợi ích cho việc này: Bạn đã có tài liệu tham khảo trong phần đầu tiên như thế sẽ không cần phải khai báo sau với dòng khác. Nó rất thuận tiện khi lặp qua máng đối tượng.

Nhược điểm:

Điều này sẽ phá vỡ bất cứ khi nào tham chiếu là sai - falsey (không xác định, v.v.). Nó có thể được sử dụng như một lợi thế mặc dù. Tuy nhiên, nó sẽ làm cho nó khó hơn một chút để đọc. Và cũng tùy thuộc vào trình duyệt, nó có thể "không" được tối ưu hóa để hoạt động nhanh hơn trình duyệt gốc.


12

Cách sử dụng jQuery $.map:

var data = [1, 2, 3, 4, 5, 6, 7];

var newData = $.map(data, function(element) {
    if (element % 2 == 0) {
        return element;
    }
});

// newData = [2, 4, 6];

1
Tôi nghĩ rằng đầu ra có nhiều khả năng là [undefined, 2, undefined, 4, undefined, 6, undefined].

10

Sử dụng các vòng lặp với phá hủy ECMAScript 6 và toán tử trải

Việc phá hủy và sử dụng toán tử lây lan đã được chứng minh khá hữu ích cho những người mới sử dụng ECMAScript 6 vì nó dễ đọc / thẩm mỹ hơn, mặc dù một số cựu chiến binh JavaScript có thể coi nó là mớ hỗn độn. Người mới hoặc một số người khác có thể thấy nó hữu ích.

Các ví dụ sau sẽ sử dụng for...ofcâu lệnh và .forEachphương thức.

Ví dụ 6, 7 và 8 có thể được sử dụng với bất kỳ vòng lặp chức năng như .map, .filter, .reduce, .sort, .every, .some. Để biết thêm thông tin về các phương thức này, hãy xem Đối tượng Array .

Ví dụ 1:for...of Vòng lặp thông thường - không có thủ thuật nào ở đây.

let arrSimple = ['a', 'b', 'c'];

for (let letter of arrSimple) {
  console.log(letter);
}

Ví dụ 2: Tách từ thành ký tự

let arrFruits = ['apple', 'orange', 'banana'];

for (let [firstLetter, ...restOfTheWord] of arrFruits) {
  // Create a shallow copy using the spread operator
  let [lastLetter] = [...restOfTheWord].reverse();
  console.log(firstLetter, lastLetter, restOfTheWord);
}

Ví dụ 3: Vòng lặp với a keyvalue

// let arrSimple = ['a', 'b', 'c'];

// Instead of keeping an index in `i` as per example `for(let i = 0 ; i<arrSimple.length;i++)`
// this example will use a multi-dimensional array of the following format type:
// `arrWithIndex: [number, string][]`

let arrWithIndex = [
  [0, 'a'],
  [1, 'b'],
  [2, 'c'],
];

// Same thing can be achieved using `.map` method
// let arrWithIndex = arrSimple.map((i, idx) => [idx, i]);

// Same thing can be achieved using `Object.entries`
// NOTE: `Object.entries` method doesn't work on Internet Explorer  unless it's polyfilled
// let arrWithIndex = Object.entries(arrSimple);

for (let [key, value] of arrWithIndex) {
  console.log(key, value);
}

Ví dụ 4: Nhận thuộc tính đối tượng nội tuyến

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];

for (let { name, age: aliasForAge } of arrWithObjects) {
  console.log(name, aliasForAge);
}

Ví dụ 5: Nhận các thuộc tính đối tượng sâu của những gì bạn cần

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

for (let { name, tags: [firstItemFromTags, ...restOfTags] } of arrWithObjectsWithArr) {
  console.log(name, firstItemFromTags, restOfTags);
}

Ví dụ 6:Ví dụ 3 sử dụng với.forEach

let arrWithIndex = [
  [0, 'a'],
  [1, 'b'],
  [2, 'c'],
];

// Not to be confused here, `forEachIndex` is the real index
// `mappedIndex` was created by "another user", so you can't really trust it

arrWithIndex.forEach(([mappedIndex, item], forEachIndex) => {
  console.log(forEachIndex, mappedIndex, item);
});

Ví dụ 7:Ví dụ 4 sử dụng với.forEach

let arrWithObjects = [{
    name: 'Jon',
    age: 32
  },
  {
    name: 'Elise',
    age: 33
  }
];
// NOTE: Destructuring objects while using shorthand functions
// are required to be surrounded by parentheses
arrWithObjects.forEach( ({ name, age: aliasForAge }) => {
  console.log(name, aliasForAge)
});

Ví dụ 8:ví dụ 5 sử dụng với.forEach

let arrWithObjectsWithArr = [{
    name: 'Jon',
    age: 32,
    tags: ['driver', 'chef', 'jogger']
  },
  {
    name: 'Elise',
    age: 33,
    tags: ['best chef', 'singer', 'dancer']
  }
];

arrWithObjectsWithArr.forEach(({
  name,
  tags: [firstItemFromTags, ...restOfTags]
}) => {
  console.log(name, firstItemFromTags, restOfTags);
});


7

Một cách gần nhất với ý tưởng của bạn sẽ là sử dụng hàm Array.forEach()chấp nhận hàm đóng sẽ được thực thi cho từng phần tử của mảng.

myArray.forEach(
  (item) => {
    // Do something
    console.log(item);
  }
);

Một cách khả thi khác là sử dụng Array.map()hoạt động theo cùng một cách, nhưng nó cũng lấy tất cả các giá trị mà bạn trả về và trả về chúng trong một mảng mới (về cơ bản ánh xạ từng phần tử sang một phần tử mới), như sau:

var myArray = [1, 2, 3];
myArray = myArray.map(
  (item) => {
    return item + 1;
  }
);

console.log(myArray); // [2, 3, 4]

1
Điều này là sai, mapkhông làm thay đổi các thành phần của mảng, bởi vì nó trả về một mảng mới, với các mục của mảng mới đó là kết quả của việc áp dụng hàm cho các mục của mảng cũ.
Jesús Franco

Tôi chưa bao giờ nói nó không trả về một mảng mới, tôi đã đề cập đến sự thay đổi được áp dụng bởi hàm. Nhưng ở đây tôi sẽ thay đổi nó trong câu trả lời.
Ante Jablan Adamović

một lần nữa sai, bản đồ không sửa đổi hoặc biến đổi tất cả các mục trong mảng ban đầu, tôi đã đề xuất và chỉnh sửa câu trả lời nhấn mạnh bản đồ hoạt động với một bản sao của các mục gốc, hoàn toàn không bị ảnh hưởng
Jesús Franco

7

Bạn có thể gọi forEach như thế này:

forEachsẽ lặp lại trên mảng bạn cung cấp và với mỗi lần lặp, nó sẽ có elementgiá trị của lần lặp đó. Nếu bạn cần chỉ mục, bạn có thể lấy chỉ mục hiện tại bằng cách chuyển itham số thứ hai trong hàm gọi lại cho forEach.

Foreach về cơ bản là Hàm bậc cao, lấy một hàm khác làm tham số.

let theArray= [1,3,2];

theArray.forEach((element) => {
  // Use the element of the array
  console.log(element)
}

Đầu ra:

1
3
2

Bạn cũng có thể lặp lại qua một mảng như thế này:

for (let i=0; i<theArray.length; i++) {
  console.log(i); // i will have the value of each index
}

6

Nếu bạn muốn lặp qua một mảng các đối tượng có chức năng mũi tên:

let arr = [{name:'john', age:50}, {name:'clark', age:19}, {name:'mohan', age:26}];

arr.forEach((person)=>{
  console.log('I am ' + person.name + ' and I am ' + person.age + ' old');
})


6

Cú pháp lambda thường không hoạt động trong Internet Explorer 10 trở xuống.

Tôi thường sử dụng

[].forEach.call(arrayName,function(value,index){
    console.log("value of the looped element" + value);
    console.log("index of the looped element" + index);
});

Nếu bạn là một người hâm mộ jQuery và đã có một tệp jQuery đang chạy, bạn nên đảo ngược các vị trí của các tham số chỉ số và giá trị

$("#ul>li").each(function(**index, value**){
    console.log("value of the looped element" + value);
    console.log("index of the looped element" + index);
});

5

Nếu bạn có một mảng lớn, bạn nên sử dụng iteratorsđể đạt được hiệu quả. Lặp là tài sản của các bộ sưu tập JavaScript nhất định (như Map, Set, String, Array). Thậm chí, for..ofsử dụng iteratordưới mui xe.

Trình vòng lặp cải thiện hiệu quả bằng cách cho phép bạn tiêu thụ các mục trong danh sách một lần như thể chúng là một luồng. Điều làm cho một trình vòng lặp trở nên đặc biệt là cách nó đi qua một bộ sưu tập. Các vòng lặp khác cần tải toàn bộ bộ sưu tập lên phía trước để lặp qua nó, trong khi đó một vòng lặp chỉ cần biết vị trí hiện tại trong bộ sưu tập.

Bạn truy cập mục hiện tại bằng cách gọi nextphương thức của iterator . Phương thức tiếp theo sẽ trả về valuemục hiện tại và a booleanđể cho biết khi nào bạn đã kết thúc bộ sưu tập. Sau đây là một ví dụ về việc tạo một trình vòng lặp từ một mảng.

Chuyển đổi mảng thông thường của bạn thành iterator bằng values()phương thức như thế này:

    const myArr = [2,3,4]

let it = myArr.values();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

Bạn cũng có thể chuyển đổi mảng thông thường của mình thành iterator bằng cách sử dụng Symbol.iteratornhư sau:

const myArr = [2,3,4]

let it = myArr[Symbol.iterator]();

console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

Bạn cũng có thể chuyển đổi thường xuyên của bạn arraythành iteratornhư thế này:

let myArr = [8, 10, 12];

function makeIterator(array) {
    var nextIndex = 0;
    
    return {
       next: function() {
           return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    };
};

var it = makeIterator(myArr);

console.log(it.next().value);   // {value: 8, done: false}
console.log(it.next().value);   // {value: 10, done: false}
console.log(it.next().value);   // {value: 12, done: false}
console.log(it.next().value);   // {value: undefined, done: true}

LƯU Ý :

  • Iterators là kiệt sức trong tự nhiên.
  • Các đối tượng không iterabletheo mặc định. Sử dụng for..introng trường hợp đó vì thay vì các giá trị, nó hoạt động với các khóa.

Bạn có thể đọc thêm về iteration protocol đây .


5

Nếu bạn muốn sử dụng forEach(), nó sẽ trông như -

theArray.forEach ( element => {
    console.log(element);
});

Nếu bạn muốn sử dụng for(), nó sẽ trông như -

for(let idx = 0; idx < theArray.length; idx++){
    let element = theArray[idx];
    console.log(element);
}

4

Theo tính năng cập nhật mới ECMAScript 6 (ES6) và ECMAScript 2015, bạn có thể sử dụng các tùy chọn sau với các vòng lặp:

cho các vòng lặp

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

// Output: 0,1,2,3,4

cho ... trong các vòng lặp

let obj = {"a":1, "b":2}

for(let k in obj){
  console.log(k)
}

// Output: a,b

Mảng.forEach ()

let array = [1,2,3,4]

array.forEach((x) => {
  console.log(x);
})

// Output: 1,2,3,4

cho ... các vòng lặp

let array = [1,2,3,4]

for(let x of array){
  console.log(x);
}

// Output: 1,2,3,4

trong khi các vòng lặp

let x = 0

while(x < 5){
  console.log(x)
  x++
}

// Output: 1,2,3,4

làm ... trong khi các vòng lặp

let x = 0

do{
  console.log(x)
  x++
}while(x < 5)

// Output: 1,2,3,4

4

Hiệu suất

Hôm nay (2019/12/18) Tôi thực hiện thử nghiệm trên tôi MacOS v10.13.6 (High Sierra), trên Chrome v 79,0, Safari và Firefox v13.0.4 v71.0 (64 bit) - kết luận về tối ưu hóa (và vi-tối ưu hóa mà thường không đáng để giới thiệu nó với mã bởi vì lợi ích là nhỏ, nhưng độ phức tạp của mã tăng lên).

  • Có vẻ như truyền thống for i( Aa ) là một lựa chọn tốt để viết mã nhanh trên tất cả các trình duyệt.

  • Các giải pháp khác, như for-of( Quảng cáo ), tất cả trong nhóm C. ... thường chậm hơn 2 - 10 (và nhiều hơn) so với Aa , nhưng đối với các mảng nhỏ thì có thể sử dụng nó - vì mục đích tăng độ rõ của mã.

  • Các vòng lặp với độ dài mảng được lưu trong bộ nhớ cache n( Ab, Bb, Be ) đôi khi nhanh hơn, đôi khi không. Có lẽ trình biên dịch tự động phát hiện tình huống này và giới thiệu bộ nhớ đệm. Sự khác biệt về tốc độ giữa các phiên bản được lưu trong bộ nhớ cache và không có bộ nhớ cache ( Aa, Ba, Bd ) là khoảng ~ 1%, do đó, có vẻ như giới thiệu nlà một tối ưu hóa vi mô .

  • Các i--giải pháp tương tự trong đó vòng lặp bắt đầu từ phần tử mảng cuối cùng ( Ac, Bc ) thường chậm hơn ~ 30% so với các giải pháp chuyển tiếp - có lẽ lý do là cách bộ nhớ cache của bộ nhớ CPU hoạt động - đọc bộ nhớ chuyển tiếp là tối ưu hơn cho bộ nhớ đệm CPU). Không nên sử dụng các giải pháp đó.

Chi tiết

Trong các thử nghiệm, chúng tôi tính toán tổng các phần tử mảng. Tôi thực hiện kiểm tra các mảng nhỏ (10 phần tử) và mảng lớn (phần tử 1M) và chia chúng thành ba nhóm:

  • A - forkiểm tra
  • B - whilekiểm tra
  • C - phương pháp khác / thay thế

Kết quả trình duyệt chéo

Kết quả cho tất cả các trình duyệt được thử nghiệm

Nhập mô tả hình ảnh ở đâytrình duyệt **

Mảng có 10 phần tử

Kết quả cho Chrome. Bạn có thể thực hiện kiểm tra trên máy của bạn ở đây .

Nhập mô tả hình ảnh ở đây

Mảng có 1.000.000 phần tử

Kết quả cho Chrome. Bạn có thể thực hiện kiểm tra trên máy của bạn ở đây

Nhập mô tả hình ảnh ở đây


4

Tóm lược:

Khi lặp qua một mảng, chúng ta thường muốn thực hiện một trong các mục tiêu sau:

  1. Chúng tôi muốn lặp lại mảng và tạo một mảng mới:

    Array.prototype.map

  2. Chúng tôi muốn lặp lại mảng và không tạo một mảng mới:

    Array.prototype.forEach

    for..of vòng

Trong JavaScript, có nhiều cách để thực hiện cả hai mục tiêu này. Tuy nhiên, một số tiện lợi hơn những cái khác. Dưới đây bạn có thể tìm thấy một số phương thức thường được sử dụng (IMO thuận tiện nhất) để thực hiện lặp lại mảng trong JavaScript.

Tạo mảng mới: Map

map()là một hàm nằm trên Array.prototypeđó có thể biến đổi mọi phần tử của một mảng và sau đó trả về một mảng mới . map()lấy làm đối số một hàm gọi lại và hoạt động theo cách sau:

let arr = [1, 2, 3, 4, 5];

let newArr = arr.map((element, index, array) => {
  return element * 2;
})

console.log(arr);
console.log(newArr);

Cuộc gọi lại mà chúng ta đã truyền vào map()như một đối số được thực thi cho mọi phần tử. Sau đó, một mảng được trả về có cùng độ dài với mảng ban đầu. Trong phần tử mảng mới này được chuyển đổi bởi hàm gọi lại được truyền vào dưới dạng đối số map().

Sự khác biệt khác biệt giữa mapvà một cơ chế vòng lặp khác như forEachvà một for..ofvòng lặp là maptrả về một mảng mới và giữ nguyên mảng cũ (trừ khi bạn thao tác rõ ràng với suy nghĩ như thế splice).

Ngoài ra, lưu ý rằng maphàm gọi lại của hàm cung cấp số chỉ mục của lần lặp hiện tại làm đối số thứ hai. Hơn nữa, đối số thứ ba có cung cấp mảng trên đó mapđược gọi không? Đôi khi những tính chất này có thể rất hữu ích.

Vòng lặp sử dụng forEach

forEachlà một hàm được đặt trên Array.prototypeđó lấy hàm gọi lại làm đối số. Sau đó, nó thực hiện chức năng gọi lại này cho mọi phần tử trong mảng. Ngược lại với map()hàm, hàm forEach không trả về gì ( undefined). Ví dụ:

let arr = [1, 2, 3, 4, 5];

arr.forEach((element, index, array) => {

  console.log(element * 2);

  if (index === 4) {
    console.log(array)
  }
  // index, and oldArray are provided as 2nd and 3th argument by the callback

})

console.log(arr);

Cũng giống như maphàm, hàm forEachgọi lại cung cấp số chỉ mục của lần lặp hiện tại làm đối số thứ hai. Ngoài ra, đối số thứ ba có cung cấp mảng forEachđược gọi không?

Lặp qua các phần tử bằng cách sử dụng for..of

Các for..ofvòng vòng qua mọi phần tử của một mảng (hoặc bất kỳ đối tượng iterable khác). Nó hoạt động theo cách sau:

let arr = [1, 2, 3, 4, 5];

for(let element of arr) {
  console.log(element * 2);
}

Trong ví dụ trên, elementlà viết tắt của một phần tử mảng và arrlà mảng mà chúng ta muốn lặp. Lưu ý rằng tênelement này là tùy ý và chúng tôi có thể đã chọn bất kỳ tên nào khác như 'el' hoặc một cái gì đó mang tính khai báo hơn khi điều này được áp dụng.

Đừng nhầm lẫn for..invòng lặp với for..ofvòng lặp. for..insẽ lặp qua tất cả các thuộc tính vô số của mảng trong khi for..ofvòng lặp sẽ chỉ lặp qua các phần tử mảng. Ví dụ:

let arr = [1, 2, 3, 4, 5];

arr.foo = 'foo';

for(let element of arr) {
  console.log(element);
}

for(let element in arr) {
  console.log(element);
}

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.