Câu hỏi Đối với mỗi một mảng trong JavaScript?


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

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

forEach(instance in theArray)

Ở đâu theArray là mảng của tôi, nhưng điều này có vẻ không chính xác.


3791
2018-02-17 13:51


gốc


Tôi đã tìm nó, nhưng tôi đã tìm forEach  và không chỉ for. như đã nói, trong c # nó có một chút khác biệt, và điều đó làm tôi bối rối :) - Dante1986
ECMAScript & nbsp; 6 có thể chứa cấu trúc "cho ... của". Xem cho ... 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
Array.ForEach chậm hơn khoảng 95% so với () cho mỗi mảng trong JavaScript. Xem bài kiểm tra hiệu suất trực tuyến này: jsperf.com/fast-array-foreach thông qua coderwall.com/p/kvzbpa - molokoloco
Đối với nhiều tình huống chậm hơn 95% sẽ không đáng kể blog.niftysnippets.org/2012/02/foreach-and-runtime-cost.html - David Sykes
Ngược lại, trong Python nó là hiệu quả hơn để sử dụng các chức năng hơn là sử dụng truyền thống cho các vòng lặp. (Xem xét điều đó i < len và i++ có thể được thực hiện bởi động cơ, thay vì thông dịch viên.) - joeytwiddle


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


TL; DR

  • Không sử dụng for-in trừ 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 lý do tại sao nó có thể cắn bạn.
  • Cược tốt nhất của bạn thường là

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

Nhưng có rất nhiề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 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à các tùy chọn cho những thứ chỉ là mảng-như, chẳng hạn như arguments đối tượng, các đối tượng có thể lặp lại 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 hiện nay, ngay cả trên động cơ ES5, bởi sự chồng chất ES2015 đến ES5. Tìm kiếm "ES2015 transpiling" / "ES6 transpiling" để biết thêm ...

Được rồi, hãy xem xét các tùy 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à thêm hai phiên bản khác vào ECMAScript 2015 ("ES2015", "ES6"):

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

Chi tiết:

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

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

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

forEach chấp nhận hàm gọi lại và tùy chọn, một giá trị để sử dụng this khi gọi lại cuộc gọ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 mảng thưa thớt. Mặc dù tôi chỉ sử dụng một đối số ở trên, 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 hàm của bạn chưa có sẵn) ).

Trừ khi bạn đang 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 kể từ khi viết bài này vào tháng 9 năm 2016), bạn có thể sử dụng một cách vui vẻ forEach trong một trang web có mục đích chung mà không có một shim. Nếu bạn cần hỗ trợ các trình duyệt lỗi thời, hãy làm mờ / lấp đầy forEach có thể dễ 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 có chứa, vì chúng được cung cấp dưới dạng đối số cho hàm lặp lại, và do đó có phạm vi độc đáo với việc lặp lại đó.

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, không được; chi tiết.

Ngoài ra, forEach là hàm "lặp qua tất cả chúng", nhưng ES5 đã định nghĩa một số hàm "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 vòng lặp lần đầu tiên trả về cuộc gọi lại false hoặc một cái gì đó falsey)
  • some (dừng vòng lặp lần đầu tiên trả về cuộc gọi lại true hoặ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 lọc trả về true và bỏ qua những thứ mà nó trả về false)
  • map (tạo một mảng mới từ các giá trị được trả về bởi cuộc gọi lại)
  • reduce (xây dựng một giá trị bằng cách liên tục gọi lại 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 để tổng hợp nội dung của một mảng và nhiều thứ khác)
  • reduceRight (như reduce, nhưng hoạt động theo thứ tự giảm dần hơn là tăng dần)

2. Sử dụng đơn giản for vòng lặp

Đô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 độ dài của mảng sẽ không thay đổi trong vòng lặp, và nó trong mã nhạy cảm với hiệu năng (không chắc), một phiên bản hơi phức tạp hơn lấy chiều dài lên phía trước có thể là nhỏ bé nhanh hơn một 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 phải loại bỏ chút nước ép cuối cùng.

Trong ES2015 trở lên, bạn có thể tạo biến chỉ mục và giá trị của mình cục bộ cho for vòng lặp:

let a = ["a", "b", "c"];
for (let index = 0; index < a.length; ++index) {
    let value = a[index];
}
//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ỉ value nhưng cũng index được tạo lại cho mỗi vòng lặp lặp lại, có nghĩa là các bao đóng được tạo ra 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 => {
        alert("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 mục đầu tiên và "Chỉ mục là: 4" nếu bạn nhấp vào cuối cùng. Điều này không không phải làm việc nếu bạn sử dụng var thay vì let.

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

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

Tuy nhiên, nó có thể hữu ích, đặc biệt cho thưa thớt mảng, nếu bạn sử dụng các biện pháp bảo vệ thích 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 are explained
        /^0$|^[1-9]\d*$/.test(key) &&    // and then hidden
        key <= 4294967294                // away below
        ) {
        console.log(a[key]);
    }
}

Lưu ý hai lần kiểm tra:

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

  2. Khóa đó là một chuỗi số cơ sở-10 ở dạng chuỗi bình thường và giá trị của nó là <= 2 ^ 32 - 2 (là 4,294,967,294). Số đó đến từ đâu? Đó là một phần của định nghĩa về chỉ mục mảng trong đặc điểm kỹ thuậ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à giá trị chỉ số lớn nhất thấp hơn 2 ^ 32 - 1, đó là giá trị tối đa của một mảng length có thể có. (Ví dụ: chiều dài của mảng phù hợp với số nguyên không dấu 32 bit.) (Đạo cụ để RobG chỉ ra trong một bình luận trên bài đăng trên blog của tôi thử nghiệm trước đó của tôi không hoàn toàn đúng.)

Đó là một chút chi phí bổ sung trên mỗi vòng lặp lặp trên hầu hết các mảng, nhưng nếu bạn có thưa thớt mảng, nó có thể là một cách hiệu quả hơn để lặp lại vì nó chỉ lặp cho các mục thực sự tồn tại. Ví dụ: đối với mảng ở trên, chúng tôi lặp tổng cộng ba lần (cho các phím "0", "10""10000"- hãy nhớ, chúng là dây), không phải 10,001 lần.

Bây giờ, bạn sẽ không muốn viết rằng mọi lúc, vì vậy bạn có thể đặt điều này trong bộ công cụ của bạn:

function arrayHasOwnIndex(array, prop) {
    return array.hasOwnProperty(prop) && /^0$|^[1-9]\d*$/.test(prop) && prop <= 4294967294; // 2^32 - 2
}

Và sau đó chúng tôi sẽ sử dụng nó như thế này:

for (key in a) {
    if (arrayHasOwnIndex(a, key)) {
        console.log(a[key]);
    }
}

Hoặc nếu bạn quan tâm đến chỉ là một thử nghiệm "đủ tốt cho hầu hết các trường hợp", bạn có thể sử dụng điều này, nhưng trong khi nó gần, nó không hoàn toàn chính xác:

for (key in a) {
    // "Good enough" for most cases
    if (String(parseInt(key, 10)) === key && a.hasOwnProperty(key)) {
        console.log(a[key]);
    }
}

4. Sử dụng for-of (sử dụng một biến lặp ngầm) (ES2015 +)

ES2015 thêm vòng lặp sang JavaScript. Cách dễ nhất để sử dụng trình vòng lặp là mới for-of tuyên bố. Nó trông như thế này:

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

Đầu ra:

một
b
c

Dưới nắp, có được một người lặp từ mảng và vòng qua nó, nhận được các giá trị từ nó. Điều này không có vấn đề khi sử dụng for-in có, bởi vì nó sử dụng một trình 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 lại thông qua mục (không phải tài sản của họ). không giống for-in trong ES5, thứ tự 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 lặp một cách rõ ràng (ES2015 +)

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

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

Trình lặp là một đối tượng khớp với định nghĩa Iterator trong đặc tả. Nó là next phương thức trả về một phương thức mới đối tượng kết quả mỗi khi bạn gọi nó. Đối tượng kết quả có thuộc tính, done, cho chúng tôi biết liệu nó có được thực hiện hay không và một tài sản value với giá trị cho lần lặp lại đó. (done là tùy chọn nếu nó sẽ là false, value là tùy chọn nếu nó sẽ là undefined.)

Nghĩa của value thay đổi tùy thuộc vào trình lặp; hỗ trợ mảng (ít nhất) ba hàm trả về trình lặp:

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

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

Ngoài mảng thực sự, cũng có giống như mảng các đối tượng có length thuộc tính và thuộc tính có tên số: NodeList các phiên bản, arguments đối tượng, vv Làm thế nào để chúng tôi lặp lại thông qua nội dung của họ?

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ể hầu hết hoặc thậm chí tất cả, của mảng tiếp cận ở 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 forEach và có liên quan (ES5 +)

    Các chức năng khác nhau trên Array.prototype là "cố ý chung" và thường có thể được sử dụng trên các đối tượng giống mảng thông qua Function#call hoặc là Function#apply. (Xem Nhớ lại các vật thể do máy chủ cung cấp ở phần cuối của 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 forEach trên một Node'S childNodes bất động 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 định 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 đơn giản for vòng lặp

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

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

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

  4. Sử dụng for-of (sử dụng một biến lặp ngầm) (ES2015 +)

    for-of sẽ sử dụng trình vòng lặp do đối tượng cung cấp (nếu có); chúng ta sẽ phải xem cách nó chơi với các đối tượng giống mảng khác nhau, đặc biệt là các đối tượng được host cung cấp như thế nào. Ví dụ, đặc điểm kỹ thuật cho NodeList từ querySelectorAll đã được cập nhật để hỗ trợ lặp lại. Thông số kỹ thuật cho HTMLCollection từ getElementsByTagName không phải.

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

    Xem # 4, chúng ta sẽ phải xem các trình vòng lặp phát ra như thế nào.

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

Lần khác, 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. Làm điều đó thật dễ dàng một cách đáng ngạc nhiên:

  1. Sử dụng slice phương pháp mảng

    Chúng ta có thể sử dụng slice phương pháp của mảng, giống như các phương pháp khác được đề cập ở trên là "cố ý chung" 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í dụ: nếu chúng tôi muốn chuyển đổi NodeList thành một mảng thực, chúng ta có thể làm điều này:

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

    Xem Nhớ lại các vật thể do máy chủ cung cấp phía dưới. Đặc biệt, lưu ý rằng điều này sẽ thất bại trong IE8 và trước đó, điều này không cho phép bạn sử dụng các đối tượng được cung cấp bởi máy chủ như this như thế.

  2. Sử dụng cú pháp mở rộng (...)

    Cũng có thể sử dụng ES2015's cú pháp truyền bá với các công cụ JavaScript hỗ trợ tính năng này:

    var trueArray = [...iterableObject];
    

    Ví dụ: nếu chúng tôi muốn chuyển đổi NodeList thành một mảng thực, với cú pháp lây lan, điều này trở nên khá ngắn gọn:

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

    Array.from (ES2015 +, nhưng dễ dàng polyfilled) tạo ra một mảng từ một đối tượng mảng giống như, tùy chọn đi qua các mục thông qua một chức năng lập bản đồ đầu tiên. Vì thế:

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

    Hoặc nếu bạn muốn nhận được một mảng tên thẻ của các phần tử với một lớp nhất định, bạn sẽ sử dụng hàm á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;
    });
    

Nhớ lại các vật thể do máy chủ cung cấp

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

Các đối tượng lưu trữ 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]] và [[Put]] đối với một đối tượng lưu trữ 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 những động từ tương tự trong spec ES2015, nhưng nó vẫn còn là trường hợp.) Một lần nữa, khi viết này, các đối tượng giống như mảng host được cung cấp trong các trình duyệt hiện đại [NodeList ví dụ, ví dụ] làm xử lý [[HasProperty]] một cách chính xác, nhưng điều quan trọng là phải kiểm tra.)


5980
2018-02-17 13:53



Tôi cũng muốn thêm rằng .forEach không thể bị phá vỡ hiệu quả. Bạn phải ném một ngoại lệ để thực hiện giờ nghỉ. - Pijusn
@ Pius: Nếu bạn muốn phá vỡ vòng lặp, bạn có thể sử dụng some. (Tôi đã có thể ưa thích cho phép phá vỡ forEach là tốt, nhưng họ, um, không hỏi tôi. ;-)) - T.J. Crowder
@ T.J.Crowder Đú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
@ user889030: Bạn cần một , sau k=0, không phải ;. Hãy nhớ rằng, lập trình là nhiều thứ, một trong số đó là chú ý đến chi tiết ... :-) - T.J. Crowder
@JimB: Điều đó được đề cập ở trên (và length không phải là một phương pháp). :-) - T.J. Crowder


Chỉnh sửa: Câu trả lời này là vô vọng. Để có 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 thức quan tâm có thể là:

  • cho mỗi
  • bản đồ
  • bộ lọc
  • zip
  • giảm
  • mỗi
  • một số

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

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

Tuy nhiên, lưu ý rằng cách tiếp cận 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 là thưa thớt, sau đó bạn có thể chạy vào các vấn đề hiệu suất với phương pháp này, vì bạn sẽ lặp qua nhiều chỉ mục không có thật không tồn tại trong mảng. Trong trường hợp này, 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 (có nghĩa là, các phần tử mảng) được tác độ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 định nghĩa 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 kế thừa. 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 "Chèn lấp". Tuy nhiên, Polyfill cho chức năng này là không đáng kể và vì nó làm cho mã dễ đọc hơn, nó là một vùng chứa tốt để bao gồm.


451
2018-02-17 13:55



tại sao lại là for(instance in objArray)  không sử dụng đúng? nó trông đơn giản hơn với tôi, nhưng tôi nghe bạn nói về nó như không phải là một cách chính xác để sử dụng? - Dante1986
Bạn có thể sử dụng bộ nhớ đệm chiều dài nội tuyến: cho (var i = 0, l = arr.length; i <l; i ++) - Robert
Dấu phẩy ở cuối dòng đầu tiên có cố ý hay là lỗi đánh máy (có thể là dấu chấm phẩy)? - 9KSoft
@ wardha-Web Đó là cố ý. Nó cho phép chúng ta khai báo nhiều biến với một var-từ khóa. Nếu chúng ta đã sử dụng dấu chấm phẩy, thì element sẽ được tuyên bố trong phạm vi toàn cầu (hay đúng hơn là, JSHint sẽ hét lên với chúng tôi trước khi nó đạt được sản xuất). - PatrikAkerstrand


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

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

CHỈNH SỬA : 

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

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

205
2018-02-17 14:01



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à đơn giản nhất và phù hợp nhất cho những người trong chúng ta sử dụng jQuery. Tôi nghĩ tất cả chúng ta nên học cách vani. Nó không bao giờ đau để mở rộng sự hiểu biết của bạn. - mopsyd
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. Đó là khuyến cáo của jQuery để sử dụng JavaScript bản địa thay vì jQuery khi nó có thể. jsperf.com/browser-diet-jquery-each-vs-for-loop - Kevin Boss
Không sử dụng jQuery khi bạn có thể sử dụng vanilla js - Noe
Theo tiêu chuẩn JS, giữ cho thư viện của bên thứ ba ra khỏi câu trả lời trừ khi không có giải pháp ngôn ngữ gốc - Steve K
Loại nhắc nhở tôi về điều này: i.stack.imgur.com/ssRUr.gif - Ajedi32


Lặp lại

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 khai báo tạm thời len biến hoặc so sánh với array.length trên mỗi lần lặp lại, một trong số đó có thể là tối ưu hóa phút.
  • Loại bỏ anh chị em từ DOM theo thứ tự ngược lại thường là hiệu quả hơn. (Trình duyệt cần làm ít chuyển dịch các phần tử trong mảng nội bộ của nó.)
  • nếu bạn sửa đổi mảng trong khi lặp, tại hoặc sau chỉ mục tôi (ví dụ: bạn xóa hoặc chèn một mục tại array[i]), sau đó một vòng lặp chuyển tiếp sẽ bỏ qua mục chuyển sang trái vào vị trí tôihoặc xử lý lại tôimục thứ đã được dịch chuyển sang phải. Trong một vòng lặp truyền thống, bạn có thể cập nhật tôi để trỏ tới mục tiếp theo cần xử lý - 1, nhưng chỉ cần đảo chiều hướng lặp lại thường là đơn giản hơn và giải pháp thanh lịch hơn.
  • Tương tự, khi sửa đổi hoặc xóa lồng nhau Các phần tử DOM, xử lý ngược lại có thể lỗi phá vỡ. Ví dụ, hãy xem xét sửa đổi innerHTML của một nút cha trước khi xử lý các con của nó. Khi đạt đến nút con, nó sẽ được tách ra khỏi DOM, đã được thay thế bởi một đứa trẻ mới được tạo ra khi nội xạ của cha mẹ được viết.
  • Nó là ngắn hơn để nhập và đọc, hơn một số tùy chọn khác có sẵn. Mặc dù nó thua forEach() và đến ES6 for ... 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ẽ được đảo ngược đối 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ữ lại trật tự của họ là kém hiệu quả. (Trình duyệt sẽ tiếp tục thay đổi mọi thứ.) Để tạo các nút DOM một cách hiệu quả và theo thứ tự, chỉ cần chuyển tiếp và nối thêm như bình thường (và cũng sử dụng một "đoạn tài liệu").
  • Vòng lặp ngược là gây nhầm lẫn cho các nhà phát triển cơ sở. (Bạn có thể xem xét rằng một lợi thế, tùy thuộc vào triển vọng của bạn.)

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

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

Mặc dù hiệu suất đạt được thường là không đáng kể, nó loại hét lên:

"Chỉ cần làm điều này với mọi mục trong danh sách, tôi không quan tâm đến mệnh lệnh!"

Tuy nhiên trong thực tế đó là không phải thực sự là một chỉ dẫn đáng tin cậy về ý định, vì nó không thể phân biệt được từ những dịp đó khi bạn làm quan tâm đến thứ tự và thực sự nhu cầu để lặp lại. Vì vậy, trên thực tế, một cấu trúc khác 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ó 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 quan trọng, và hiệu quả là một mối quan tâm (trong vòng lặp trong cùng của một trò chơi hoặc công cụ hoạt hình), sau đó nó có thể được chấp nhận để sử dụng đảo ngược cho vòng lặp như mô hình go-to của bạn. Chỉ cần nhớ rằng nhìn thấy một đảo ngược cho vòng lặp trong mã hiện tại không nhất thiết có nghĩa là rằng thứ tự không liên quan!

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

Nói chung cho mã cấp cao hơn rõ ràng và an toàn là mối quan tâm lớn hơn, tôi khuyên bạn nên sử dụng Array::forEach làm mẫu mặc định của bạn:

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

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

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


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 rằng i-- là mệnh đề ở giữa (nơi chúng ta thường thấy so sánh) và mệnh đề cuối cùng trống (nơi chúng ta thường thấy i++). Đó có nghĩa là i-- cũng được sử dụng như điều kiện để tiếp tục. Quan trọng, nó được thực hiện và kiểm tra trước mỗi lần lặp lại.

  • Làm thế nào nó có thể bắt đầu tại array.length mà không bùng nổ?

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

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

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

    Bí quyết là không giống như --i, dấu i-- -giá trị toán tử i nhưng mang lại giá trị trước sự sụt 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, trên lặp lại cuối cùng, tôi trước đây 1 và i-- biểu thức thay đổi nó thành 0 nhưng thực tế sản lượng 1 (sự thật), và vì vậy tình trạng này trôi qua. Trong lần lặp tiếp theo i-- thay đổi tôi đến -1 nhưng sản lượng 0 (falsey), khiến cho việc thực hiện ngay lập tức rơi ra khỏi đáy của vòng lặp.

    Trong chuyển tiếp truyền thống cho vòng lặp, i++ và ++i có thể hoán đổi cho nhau (như Douglas Crockford chỉ ra). Tuy nhiên, ngược lại đối với vòng lặp, bởi vì sự suy giảm của chúng ta cũng là biểu hiện tình trạng của chúng ta, chúng ta phải gắn bó với i-- nếu chúng ta muốn xử lý mục tại chỉ mục 0.


Câu đố

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

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

Tín dụng đi đến WYL cho thấy tôi những lợi ích và nỗi kinh hoàng của đảo ngược cho vòng lặp.


88
2018-05-02 14:21



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ại 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
Làm thế nào về điều này cho một vòng lặp ngược? var i = array.length; trong khi tôi--) { ... - Kabb5
Xin lỗi @ Kabb5 Tôi là AFK. Có vòng lặp đó khá rõ ràng và về cơ bản giống nhau. - joeytwiddle
Ah, mẫu mã "Arrow and a Wink" cũ tốt. Tôi thích nó! +1 từ tôi! - Matheus Avellar
Câu trả lời tương tự được đưa ra chính xác hơn đây (trên một câu hỏi khác). - joeytwiddle


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

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

Có một bắt. for..in sẽ lặp qua từng thành viên có thể đếm được của các đố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 thừa hưởng 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 hay không:

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

Ngoài ra, ECMAScript 5 đã thêm một forEach phương pháp để Array.prototypecó thể được sử dụng để liệt kê trên một mảng bằng cách sử dụng một calback (các polyfill là 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ũ):

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

Điều quan trọng cần lưu ý là Array.prototype.forEach không phá vỡ khi trả về cuộc gọi lại false. jQuery và Underscore.js cung cấp các biến thể của riêng họ trên each để cung cấp các vòng lặp có thể được đoản mạch.


71
2018-02-17 14:00



Vậy làm thế nào để thoát ra khỏi vòng lặp ECMAScript5 foreach như chúng ta sẽ cho một vòng lặp bình thường hoặc vòng lặp foreach giống như được tìm thấy trong các ngôn ngữ kiểu C? - Ciaran Gallagher
@CiaranG, trong JavaScript, nó thường thấy each phương pháp cho phép return false được sử dụng để thoát ra khỏi vòng lặp, nhưng với forEach đây không phải là một lựa chọn. Có thể sử dụng cờ bên ngoài (ví dụ: if (flag) return;, nhưng nó sẽ chỉ ngăn chặn phần còn lại của cơ quan chức năng thực hiện, forEach vẫn sẽ tiếp tục lặp qua toàn bộ bộ sưu tập. - zzzzBov


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

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 vào bộ nhớ đệm myArray.length hoặc lặp qua nó ngược.


30
2018-02-17 13:55



cho (var i = 0, length = myArray.length; i <length; i ++) nên làm điều đó - Edson Medina
@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
@joeytwiddle Có, nhưng đó là vượt ra ngoài 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 anyway. - Edson Medina
@ EdsonMedina Lời xin lỗi của tôi, tôi đã hoàn toàn sai. Sử dụng , sau khi một bài tập không phải giới thiệu một toàn cầu mới, vì vậy đề xuất của bạn chỉ là khỏe! Tôi đã nhầm lẫn điều này cho một vấn đề khác nhau: = sau một bài tập tạo ra một toàn cầu mới. - joeytwiddle
Hãy coi chừng biến i là không phải địa phương cho vòng lặp. JavaScript không có phạm vi khối. Nó có lẽ tốt hơn để khai báo var i, length, arrayItem; trước vòng lặp để tránh sự hiểu lầm này. - James


A cho mỗi thực hiện (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
  }
);

25
2018-04-10 00:26



iterator trong này, đang làm một tính toán chiều 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
@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 điều này câu hỏi - nmoliveira
Việc thực hiện đầy đủ và đúng có thể được tìm thấy ở đây: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… - marciowb


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

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

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


25
2018-03-10 02:37



Nó không hoạt động nếu bạn có một mảng thưa thớt như [1, 2, undefined, 3]. - M. Grzywaczewski
... hoặc thực sự bất cứ điều gì falsey: [1, 2, 0, 3] hoặc là [true, true, false, true] - joeytwiddle