JavaScript - Các nét của myArray.forEach so với vòng lặp for


88

Tôi đã thấy rất nhiều câu hỏi đề xuất sử dụng:

for (var i = 0; i < myArray.length; i++){ /* ... */ }

thay vì:

for (var i in myArray){ /* ... */ }

cho mảng, do lặp lại không nhất quán ( xem tại đây ).


Tuy nhiên, tôi dường như không thể tìm thấy bất kỳ điều gì có vẻ thích vòng lặp hướng đối tượng:

myArray.forEach(function(item, index){ /* ... */ });

Điều đó có vẻ trực quan hơn đối với tôi.

Đối với dự án hiện tại của tôi, khả năng tương thích của IE8 là rất quan trọng và tôi đang xem xét sử dụng polyfill của Mozilla , tuy nhiên tôi không chắc chắn 100% cách thức hoạt động của nó.

  • Có sự khác biệt nào giữa vòng lặp for chuẩn (ví dụ đầu tiên ở trên) và việc triển khai Array.prototype.forEach bởi các trình duyệt hiện đại không?
  • Có sự khác biệt nào giữa triển khai trình duyệt hiện đại và triển khai của Mozilla được liên kết ở trên (đối với IE8) không?
  • Hiệu suất không phải là một vấn đề lớn, chỉ là sự nhất quán với các thuộc tính được lặp lại.

6
Nó không thể breakthoát ra forEach. Nhưng một lợi thế lớn là tạo một phạm vi mới với chức năng. Với polyfill, bạn sẽ không gặp bất kỳ vấn đề nào (ít nhất là tôi chưa gặp phải bất kỳ vấn đề nào).
hgoebl

Các vấn đề bạn có thể gặp phải với IE cũ hơn, không phải là chính miếng đệm holes, mà undefinedlà phương thức khởi tạo mảng / chữ wrt bị hỏng ở đâu và các phương thức bị hỏng khác, như slicehasOwnPropertywrt đối với các đối tượng DOM giống mảng. Thử nghiệm của tôi và es5 shimđã cho thấy các phương pháp chêm như vậy phù hợp với đặc điểm kỹ thuật (không phải thử nghiệm miếng đệm của MDN).
Xotic750

1
Và cố gắng thoát ra khỏi forvòng lặp, đó là những gì somedành cho.
Xotic750

1
"Tuy nhiên, tôi dường như không thể tìm thấy bất cứ điều gì có vẻ thích vòng lặp hướng đối tượng hơn:" Tôi thà gọi nó theo cách chức năng hơn là mệnh lệnh.
Memke

Bạn có thể sử dụng Array.find()để thoát ra khỏi vòng lặp sau khi tìm thấy kết quả phù hợp đầu tiên.
Mottie

Câu trả lời:


121

Sự khác biệt cơ bản nhất giữa forvòng lặp và forEachphương pháp là, với phương pháp trước đây, bạn có thể breakthoát khỏi vòng lặp. Bạn có thể mô phỏng continuebằng cách đơn giản quay lại từ hàm được truyền tới forEach, nhưng không có cách nào để dừng lặp lại hoàn toàn.

Ngoài ra, cả hai đều thực hiện hiệu quả cùng một chức năng. Một sự khác biệt nhỏ khác liên quan đến phạm vi của chỉ mục (và tất cả các biến chứa các biến) trong vòng lặp for, do biến lưu trữ.

// 'i' is scoped to the containing function
for (var i = 0; i < arr.length; i++) { ... }

// 'i' is scoped to the internal function
arr.forEach(function (el, i) { ... });

Tuy nhiên, tôi thấy điều đó forEachbiểu cảm hơn nhiều — nó thể hiện ý định của bạn để lặp lại qua từng phần tử của một mảng và nó cung cấp cho bạn một tham chiếu đến phần tử, không chỉ chỉ mục. Nhìn chung, nó chủ yếu phụ thuộc vào sở thích cá nhân, nhưng nếu bạn có thể sử dụng forEach, tôi khuyên bạn nên sử dụng nó.


Có một vài khác biệt đáng kể giữa hai phiên bản, đặc biệt là về hiệu suất. Trên thực tế, vòng lặp for đơn giản hoạt động tốt hơn đáng kể so với forEachphương pháp này, như được chứng minh bằng phép thử jsperf này .

Hiệu suất như vậy có cần thiết cho bạn hay không là do bạn quyết định, và trong hầu hết các trường hợp, tôi sẽ ưu tiên sự biểu cảm hơn tốc độ. Sự khác biệt về tốc độ này có thể là do sự khác biệt nhỏ về ngữ nghĩa giữa vòng lặp cơ bản và phương thức khi hoạt động trên các mảng thưa thớt, như đã nêu trong câu trả lời này .

Nếu bạn không cần hành vi của forEachvà / hoặc bạn cần thoát ra khỏi vòng lặp sớm, bạn có thể sử dụng Lo-Dash's _.eachđể thay thế, cũng sẽ hoạt động trên nhiều trình duyệt. Nếu bạn đang sử dụng jQuery, nó cũng cung cấp một hàm tương tự $.each, chỉ cần lưu ý sự khác biệt về các đối số được truyền cho hàm gọi lại trong mỗi biến thể.

(Đối với forEachpolyfill, nó sẽ hoạt động trong các trình duyệt cũ hơn mà không gặp sự cố, nếu bạn chọn đi theo con đường đó.)


1
Cần lưu ý rằng các phiên bản thư viện eachkhông hoạt động theo cách giống như đặc tả ECMA5 forEach, chúng có xu hướng coi tất cả các mảng là dày đặc (để tránh lỗi IE, miễn là bạn biết). Có thể là một "gotcha" nếu không. Như một tài liệu tham khảo github.com/es-shims/es5-shim/issues/190
Xotic750

7
Ngoài ra, có một số phương thức nguyên mẫu cho phép bạn ngắt, chẳng hạn như phương thức Array.prototype.somenày sẽ lặp lại cho đến khi bạn trả về một giá trị trung thực hoặc cho đến khi nó lặp lại hoàn toàn qua mảng. Array.prototype.everytương tự như Array.prototype.somenhưng dừng nếu bạn trả về một giá trị giả.
axelduch

Nếu bạn muốn xem một số kết quả kiểm tra trên các trình duyệt khác nhau và những miếng dán
Xotic750

Ngoài ra, việc sử dụng Array.prototype.forEach sẽ đặt mã của bạn vào phần mở rộng. Ví dụ: nếu bạn đang viết mã để nhúng vào một trang, có khả năng Array.prototype.forEach bị ghi đè bằng thứ khác.
Marble Daemon

2
@MarbleDaemon Cơ hội xảy ra là rất nhỏ đến mức không thể thực hiện được và do đó không đáng kể. Việc ghi đè Array.prototype.forEachvới một số phiên bản không tương thích sẽ có khả năng phá vỡ nhiều thư viện nên dù sao thì việc tránh nó vì lý do đó cũng không giúp ích được gì.
Alexis King,

12

Bạn có thể sử dụng hàm foreach tùy chỉnh của mình, hàm này sẽ hoạt động tốt hơn nhiều so với Array.forEach

Bạn nên thêm điều này một lần vào mã của mình. Thao tác này sẽ thêm chức năng mới vào Mảng.

function foreach(fn) {
    var arr = this;
    var len = arr.length;
    for(var i=0; i<len; ++i) {
        fn(arr[i], i);
    }
}

Object.defineProperty(Array.prototype, 'customForEach', {
    enumerable: false,
    value: foreach
});

Sau đó, bạn có thể sử dụng nó ở bất cứ đâu như Array.forEach

[1,2,3].customForEach(function(val, i){

});

Sự khác biệt duy nhất nó là nhanh hơn 3 lần. https://jsperf.com/native-arr-foreach-vs-custom-foreach

CẬP NHẬT: Trong phiên bản Chrome mới, hiệu suất của .forEach () đã được cải thiện. Tuy nhiên, giải pháp có thể cung cấp hiệu suất bổ sung trong các trình duyệt khác.

JSPerf


4

Nó được nhiều nhà phát triển (ví dụ Kyle Simpson) gợi ý sử dụng .forEachđể chỉ ra rằng mảng sẽ có tác dụng phụ và .mapcho các hàm thuần túy. forvòng lặp phù hợp cũng như một giải pháp có mục đích chung cho số lượng vòng lặp đã biết hoặc bất kỳ trường hợp nào khác không phù hợp vì nó dễ giao tiếp hơn vì hỗ trợ rộng rãi trên phần lớn các ngôn ngữ lập trình.

ví dụ

/* For Loop known number of iterations */
const numberOfSeasons = 4;
for (let i = 0; i < numberOfSeasons; i++) {
  //Do Something
}

/* Pure transformation */
const arrayToBeUppercased = ['www', 'html', 'js', 'us'];
const acronyms = arrayToBeUppercased.map((el) => el.toUpperCase));

/* Impure, side-effects with .forEach */
const acronymsHolder = [];
['www', 'html', 'js', 'us'].forEach((el) => acronymsHolder.push(el.toUpperCase()));

Quy ước khôn ngoan, điều này có vẻ tốt nhất, tuy nhiên cộng đồng vẫn chưa thực sự giải quyết một quy ước về các for invòng lặp giao thức mới hơn . Nói chung, tôi nghĩ rằng đó là một ý tưởng tốt để tuân theo các khái niệm FP mà cộng đồng JS dường như sẵn sàng chấp nhận.

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.