JavaScript - ความแตกต่างของ myArray.forEach vs for loop


88

ฉันเห็นคำถามมากมายที่แนะนำให้ใช้:

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

แทน:

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

สำหรับอาร์เรย์เนื่องจากการวนซ้ำไม่สอดคล้องกัน ( ดูที่นี่ )


อย่างไรก็ตามฉันไม่พบสิ่งที่ดูเหมือนจะชอบลูปเชิงวัตถุ:

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

ซึ่งดูเหมือนง่ายกว่าสำหรับฉัน

สำหรับโครงการปัจจุบันของฉันความเข้ากันได้ของ IE8 เป็นสิ่งสำคัญและฉันกำลังพิจารณาใช้polyfill ของ Mozillaแต่ฉันไม่แน่ใจ 100% ว่าจะได้ผลอย่างไร

  • มีความแตกต่างระหว่างมาตรฐานสำหรับลูป (ตัวอย่างแรกด้านบน) และการใช้งาน Array.prototype.forEach โดยเบราว์เซอร์สมัยใหม่หรือไม่
  • มีความแตกต่างระหว่างการใช้งานเบราว์เซอร์สมัยใหม่และการใช้งานของ Mozilla ที่เชื่อมโยงกับด้านบน (โดยคำนึงถึง IE8 เป็นพิเศษ) หรือไม่
  • ประสิทธิภาพไม่ได้เป็นปัญหามากนักเพียง แต่สอดคล้องกับคุณสมบัติที่มีการทำซ้ำ

6
มันไม่ได้เป็นไปได้ที่จะออกจากbreak forEachแต่ข้อดีอย่างมากคือการสร้างขอบเขตใหม่ด้วยฟังก์ชัน ด้วย polyfill คุณไม่ควรมีปัญหาใด ๆ (อย่างน้อยฉันก็ไม่พบอะไรเลย)
hgoebl

ปัญหาที่คุณสามารถมีได้กับ IE รุ่นเก่าไม่ใช่ shim แต่เป็นตัวสร้างอาร์เรย์ที่เสีย / ตัวอักษร wrt holesที่undefinedควรจะเป็นและวิธีการอื่น ๆ ที่ใช้งานไม่ได้เช่นsliceและเขียนhasOwnPropertyไปยังวัตถุ DOM ที่เหมือนอาร์เรย์ การทดสอบของฉันและes5 shimได้แสดงวิธีการ shimmed ที่สอดคล้องกับข้อกำหนด (ไม่ได้ทดสอบ shim ของ MDN)
Xotic750

1
และพยายามที่จะหลุดออกจากforวงนั่นคือสิ่งที่someมีไว้เพื่อ
Xotic750

1
"อย่างไรก็ตามฉันไม่พบสิ่งที่ดูเหมือนจะชอบการวนซ้ำเชิงวัตถุ:" ฉันอยากจะเรียกมันว่าวิธีการใช้งานกับความจำเป็น
Memke

คุณสามารถใช้Array.find()เพื่อแยกออกจากลูปหลังจากพบการแข่งขันครั้งแรก
Mottie

คำตอบ:


122

ความแตกต่างที่สำคัญที่สุดระหว่างการforวนซ้ำและforEachวิธีการคือในอดีตคุณอาจbreakออกจากลูป คุณสามารถจำลองได้continueโดยเพียงแค่ย้อนกลับจากฟังก์ชั่นที่ส่งไปยังforEachแต่ไม่มีวิธีใดที่จะหยุดการวนซ้ำได้ทั้งหมด

นอกเหนือจากนั้นทั้งสองยังทำงานได้อย่างมีประสิทธิภาพเหมือนกัน ความแตกต่างเล็กน้อยอีกประการหนึ่งเกี่ยวข้องกับขอบเขตของดัชนี (และตัวแปรที่มีทั้งหมด) ใน for loop เนื่องจากการยกตัวแปร

// '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) { ... });

อย่างไรก็ตามฉันพบว่าforEachมันแสดงออกได้ดีกว่ามากซึ่งแสดงถึงความตั้งใจของคุณที่จะวนซ้ำผ่านแต่ละองค์ประกอบของอาร์เรย์และให้ข้อมูลอ้างอิงถึงองค์ประกอบนั้นไม่ใช่แค่ดัชนี โดยรวมแล้วส่วนใหญ่จะขึ้นอยู่กับรสนิยมส่วนตัว แต่ถ้าคุณสามารถใช้ได้forEachฉันขอแนะนำให้ใช้


มีความแตกต่างที่สำคัญเล็กน้อยระหว่างสองเวอร์ชันโดยเฉพาะเกี่ยวกับประสิทธิภาพ ในความเป็นจริง Simple for loop ทำงานได้ดีกว่าforEachวิธีการมากดังที่แสดงโดยการทดสอบ jsperfนี้

การแสดงดังกล่าวจำเป็นสำหรับคุณหรือไม่นั้นขึ้นอยู่กับคุณในการตัดสินใจและโดยส่วนใหญ่ฉันจะชอบการแสดงออกมากกว่าความเร็ว ความแตกต่างของความเร็วนี้น่าจะเกิดจากความแตกต่างทางความหมายเล็กน้อยระหว่างลูปพื้นฐานและวิธีการเมื่อทำงานกับอาร์เรย์แบบกระจัดกระจายดังที่ระบุไว้ในคำตอบนี้

หากคุณไม่ต้องการพฤติกรรมของforEachและ / หรือคุณจำเป็นต้องแยกตัวออกจากวงในช่วงต้นคุณสามารถใช้ Lo-Dash _.eachเป็นทางเลือกซึ่งจะทำงานข้ามเบราว์เซอร์ได้เช่นกัน หากคุณกำลังใช้ jQuery มันยังให้สิ่งที่คล้ายกัน$.eachเพียงสังเกตความแตกต่างของอาร์กิวเมนต์ที่ส่งไปยังฟังก์ชันเรียกกลับในแต่ละรูปแบบ

(สำหรับforEachpolyfill ควรใช้งานได้ในเบราว์เซอร์รุ่นเก่าโดยไม่มีปัญหาหากคุณเลือกไปเส้นทางนั้น)


1
เป็นที่น่าสังเกตว่าเวอร์ชันไลบรารีeachไม่ทำงานในลักษณะเดียวกับข้อกำหนด ECMA5 ของforEachพวกเขามักจะถือว่าอาร์เรย์ทั้งหมดมีความหนาแน่นสูง (เพื่อหลีกเลี่ยงข้อบกพร่องของ IE หากคุณทราบ) สามารถเป็น "gotcha" เป็นอย่างอื่นได้ เป็นแหล่งอ้างอิงgithub.com/es-shims/es5-shim/issues/190
Xotic750

7
นอกจากนี้ยังมีวิธีต้นแบบบางอย่างที่อนุญาตให้คุณทำลายเช่นArray.prototype.someซึ่งจะวนซ้ำจนกว่าคุณจะคืนค่าที่แท้จริงหรือจนกว่าจะวนซ้ำอย่างสมบูรณ์ผ่านอาร์เรย์ Array.prototype.everyคล้ายกับArray.prototype.someแต่จะหยุดถ้าคุณส่งคืนค่าที่ไม่ถูกต้อง
axelduch

หากคุณต้องการดูผลการทดสอบในเบราว์เซอร์ที่แตกต่างกันและ shims ดังกล่าวคุณสามารถดูได้ที่นี่ci.testling.com/Xotic750/util-x
Xotic750

นอกจากนี้การใช้ Array.prototype.forEach จะทำให้รหัสของคุณอยู่ในความเมตตาของส่วนขยาย ตัวอย่างเช่นหากคุณกำลังเขียนโค้ดเพื่อฝังลงในเพจมีโอกาสที่ Array.prototype.forEach ถูกเขียนทับด้วยอย่างอื่น
Marble Daemon

2
@MarbleDaemon โอกาสที่จะน้อยมากจนเป็นไปไม่ได้อย่างมีประสิทธิภาพดังนั้นจึงเป็นเรื่องเล็กน้อย การเขียนทับArray.prototype.forEachด้วยเวอร์ชันที่เข้ากันไม่ได้จะมีโอกาสทำลายไลบรารีจำนวนมากที่การหลีกเลี่ยงด้วยเหตุผลนั้นจะไม่ช่วยได้
Alexis King

12

คุณสามารถใช้ฟังก์ชัน foreach ที่กำหนดเองซึ่งจะทำงานได้ดีกว่ามากจากนั้น Array.forEach

คุณควรเพิ่มครั้งเดียวในรหัสของคุณ สิ่งนี้จะเพิ่มฟังก์ชันใหม่ให้กับ Array

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
});

จากนั้นคุณสามารถใช้งานได้ทุกที่เช่น Array.forEach

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

});

ความแตกต่างเพียงอย่างเดียวคือเร็วขึ้น 3 เท่า https://jsperf.com/native-arr-foreach-vs-custom-foreach

อัปเดต: ใน Chrome เวอร์ชันใหม่ประสิทธิภาพของ. forEach () ได้รับการปรับปรุง อย่างไรก็ตามโซลูชันนี้สามารถให้ประสิทธิภาพเพิ่มเติมในเบราว์เซอร์อื่น ๆ

JSPerf


4

มีการแนะนำโดยนักพัฒนาหลายคน (เช่น Kyle Simpson) เพื่อใช้.forEachเพื่อระบุว่าอาร์เรย์จะมีผลข้างเคียงและ.mapสำหรับฟังก์ชันที่บริสุทธิ์ forลูปเข้ากันได้ดีกับโซลูชันที่ใช้งานทั่วไปสำหรับจำนวนลูปที่ทราบหรือกรณีอื่น ๆ ที่ไม่เหมาะสมเนื่องจากสื่อสารได้ง่ายขึ้นเนื่องจากมีการสนับสนุนที่กว้างขวางในภาษาโปรแกรมส่วนใหญ่

เช่น

/* 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()));

การประชุมอย่างชาญฉลาดดูเหมือนว่าจะดีที่สุดอย่างไรก็ตามชุมชนยังไม่ได้ตัดสินตามอนุสัญญาเกี่ยวกับfor inลูปโปรโตคอลการวนซ้ำที่ใหม่กว่า โดยทั่วไปฉันคิดว่าเป็นความคิดที่ดีที่จะปฏิบัติตามแนวคิด FP ที่ชุมชน JS ดูเหมือนจะเปิดกว้างสำหรับการนำไปใช้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.