สำหรับแต่ละอาร์เรย์ใน JavaScript


4682

ฉันจะวนซ้ำรายการทั้งหมดในอาร์เรย์โดยใช้ JavaScript ได้อย่างไร

ฉันคิดว่ามันเป็นเช่นนี้:

forEach(instance in theArray)

theArrayอาร์เรย์ของฉันอยู่ที่ไหนแต่ดูเหมือนว่าจะไม่ถูกต้อง


16
ฉันไม่มองมัน แต่ฉันมองหาและไม่เพียงforEach forตามที่ระบุไว้ใน c # มันเป็นความแตกต่างกันเล็กน้อยและที่ฉันสับสน :)
Dante1986

6
ECMAScript & nbsp; 6 อาจจะมีโครงสร้าง "สำหรับ ... ของ" ดูสำหรับ ... จาก (MDN) สำหรับข้อมูลเพิ่มเติม คุณสามารถทดลองใช้กับ Firefox เวอร์ชันล่าสุดได้แล้ว
Slaven Rezic

36
Array.ForEach ช้ากว่าประมาณ 95% สำหรับ () ในสำหรับแต่ละอาร์เรย์ใน JavaScript ดูการทดสอบประสิทธิภาพออนไลน์นี้: jsperf.com/fast-array-foreachผ่านcoderwall.com/p/kvzbpa
molokoloco

77
สำหรับหลาย ๆ สถานการณ์ที่ช้าลง 95% นั้นจะไม่เป็นสิ่งสำคัญในบล็อก blog.niftysnippets.org/2012/02/foreach-and-runtime-cost.html
David Sykes

7
ในทางตรงกันข้ามในPythonมันมีประสิทธิภาพมากกว่าในการใช้ฟังก์ชั่นมากกว่าที่จะใช้แบบดั้งเดิมสำหรับลูป (พิจารณาว่าi < lenและi++สามารถทำได้โดยเครื่องยนต์มากกว่าโดยล่าม.)
joeytwiddle

คำตอบ:


7019

TL; DR

  • อย่าใช้for-inจนกว่าคุณจะใช้มันด้วยการป้องกันหรืออย่างน้อยก็รู้ว่าทำไมมันอาจกัดคุณ
  • โดยปกติแล้วการเดิมพันที่ดีที่สุดของคุณ

    • for-ofห่วง (ES2015 + เท่านั้น)
    • Array#forEach( spec| MDN) (หรือญาติsomeและอย่างนั้น) (ES5 + เท่านั้น)
    • forวนเวียนเรียบง่าย
    • หรือfor-inด้วยการป้องกัน

แต่ยังมีอีกมากมายให้สำรวจอ่านต่อ ...


JavaScript มีซีแมนทิกส์ที่ทรงพลังสำหรับการวนลูปผ่านอาร์เรย์และวัตถุคล้ายอาร์เรย์ ฉันแบ่งคำตอบออกเป็นสองส่วน: ตัวเลือกสำหรับอาร์เรย์ของแท้และตัวเลือกสำหรับสิ่งต่าง ๆเช่นอาเรย์argumentsวัตถุออบเจกต์อื่น ๆ (ES2015 +) คอลเลกชัน DOM และอื่น ๆ

ฉันได้อย่างรวดเร็วจะได้ทราบว่าคุณสามารถใช้ ES2015 ตัวเลือกในขณะนี้แม้ในเครื่องมือ ES5 โดยtranspiling ES2015 เพื่อ ES5 ค้นหา "transpiling ES2015" / "transpiling ES6" สำหรับข้อมูลเพิ่มเติม ...

เอาล่ะมาดูตัวเลือกของเรา:

สำหรับอาร์เรย์ที่เกิดขึ้นจริง

คุณมีสามตัวเลือกในECMAScript 5 ("ES5") รุ่นที่ได้รับการสนับสนุนอย่างกว้างขวางที่สุดในขณะนี้และอีกสองตัวเลือกเพิ่มเติมในECMAScript 2015 ("ES2015", "ES6"):

  1. การใช้งานforEachและที่เกี่ยวข้อง (ES5 +)
  2. ใช้การforวนรอบง่าย ๆ
  3. ใช้for-in อย่างถูกต้อง
  4. ใช้for-of(ใช้ตัววนซ้ำโดยนัย) (ES2015 +)
  5. ใช้ตัววนซ้ำอย่างชัดเจน (ES2015 +)

รายละเอียด:

1. การใช้งานforEachและที่เกี่ยวข้อง

ในสภาพแวดล้อมที่ทันสมัยราง ๆ (ดังนั้นไม่ใช่ IE8) ที่คุณสามารถเข้าถึงArrayคุณลักษณะที่เพิ่มโดย ES5 (โดยตรงหรือใช้โพลีฟิล) คุณสามารถใช้forEach( spec| MDN):

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

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

ถ้าคุณไม่สนับสนุนเบราว์เซอร์ที่ล้าสมัยอย่าง IE8 (ซึ่ง NetApps แสดงที่ส่วนแบ่งการตลาดมากกว่า 4% จากการเขียนนี้ในเดือนกันยายน 2559) คุณสามารถใช้งานได้อย่างมีความสุขforEachในหน้าเว็บเอนกประสงค์ที่ไม่มีหน้าจอ หากคุณต้องการรองรับเบราว์เซอร์ที่ล้าสมัยการ shimming / polyfilling forEachสามารถทำได้อย่างง่ายดาย (ค้นหา "es5 shim" สำหรับตัวเลือกต่างๆ)

forEach มีประโยชน์ที่คุณไม่ต้องประกาศการจัดทำดัชนีและตัวแปรค่าในขอบเขตที่มีเนื่องจากพวกเขากำลังให้เป็นข้อโต้แย้งกับฟังก์ชั่นการทำซ้ำและเพื่อกำหนดขอบเขตอย่างมากเพียงแค่การทำซ้ำนั้น

หากคุณกังวลเกี่ยวกับค่าใช้จ่ายรันไทม์ในการโทรเรียกใช้ฟังก์ชั่นสำหรับแต่ละรายการอาเรย์ รายละเอียด

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

  • every(หยุดการวนซ้ำในครั้งแรกที่โทรกลับfalseหรือบางสิ่งบางอย่างผิดพลาด)
  • some(หยุดวนซ้ำในครั้งแรกที่โทรกลับtrueหรือสิ่งที่เป็นความจริง)
  • filter(สร้างอาร์เรย์ใหม่ซึ่งรวมถึงองค์ประกอบที่ฟังก์ชันตัวกรองส่งคืนtrueและละเว้นอาร์เรย์ที่ส่งคืนfalse)
  • map (สร้างอาร์เรย์ใหม่จากค่าที่ส่งคืนโดยการเรียกกลับ)
  • reduce (สร้างค่าโดยการโทรติดต่อกลับซ้ำ ๆ ผ่านค่าก่อนหน้าดูข้อมูลจำเพาะสำหรับรายละเอียดมีประโยชน์สำหรับการรวมเนื้อหาของอาร์เรย์และสิ่งอื่น ๆ )
  • reduceRight(เช่นreduceแต่ทำงานในลำดับมากไปน้อยมากกว่าเรียงลำดับขึ้นมา)

2. ใช้การforวนรอบง่าย ๆ

บางครั้งวิธีเก่า ๆ นั้นดีที่สุด:

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

ถ้าความยาวของอาร์เรย์จะไม่เปลี่ยนแปลงในช่วงห่วงและก็ในรหัสประสิทธิภาพที่สำคัญ (น่า) รุ่นที่ซับซ้อนกว่าเล็กน้อยโลภความยาวขึ้นหน้าอาจจะมีเล็ก ๆบิตเร็วขึ้น:

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

และ / หรือนับถอยหลัง:

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

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

ใน ES2015 และสูงกว่าคุณสามารถสร้างดัชนีและค่าตัวแปรของคุณแบบโลคัลได้for:

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"

และเมื่อคุณทำเช่นนั้นไม่เพียงvalueแต่indexจะถูกสร้างขึ้นใหม่สำหรับการวนซ้ำแต่ละครั้งความหมายการปิดที่สร้างขึ้นในร่างกายลูปจะอ้างอิงถึงการสร้างindex(และvalue) สำหรับการทำซ้ำเฉพาะนั้น:

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

หากคุณมีห้า divs คุณจะได้รับ "ดัชนีคือ: 0" หากคุณคลิกที่แรกและ "ดัชนีคือ: 4" ถ้าคุณคลิกที่สุดท้าย นี้ไม่ได้ทำงานถ้าคุณใช้แทนvarlet

3. ใช้for-in อย่างถูกต้อง

คุณจะได้รับคนที่บอกให้คุณใช้for-inแต่นั่นไม่ใช่สิ่งที่for-inเป็น for-inวนซ้ำผ่านคุณสมบัติที่นับได้ของวัตถุไม่ใช่ดัชนีของอาร์เรย์ ไม่รับประกันคำสั่งไม่แม้แต่ใน ES2015 (ES6) ES2015 + กำหนดคำสั่งให้กับคุณสมบัติของวัตถุ (ผ่าน[[OwnPropertyKeys]], [[Enumerate]]และสิ่งที่ใช้พวกเขาชอบObject.getOwnPropertyKeys) แต่ไม่ได้กำหนดว่าfor-inจะทำตามคำสั่งนั้น แม้ว่า ES2020 ทำ (รายละเอียดในคำตอบอื่น ๆนี้)

กรณีการใช้งานจริงสำหรับfor-inอาร์เรย์เท่านั้น:

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

ดูจากตัวอย่างแรกเท่านั้น: คุณสามารถใช้for-inเพื่อเยี่ยมชมองค์ประกอบอาเรย์เหล่านั้นหากคุณใช้การป้องกันที่เหมาะสม:

// `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]);
    }
}

บันทึกการตรวจสอบสามรายการ:

  1. วัตถุนั้นมีคุณสมบัติเป็นของตนเองโดยใช้ชื่อนั้น (ไม่ใช่สิ่งที่สืบทอดมาจากต้นแบบ) และ

  2. ว่ากุญแจนั้นเป็นตัวเลขทศนิยมทั้งหมด (เช่นรูปแบบสตริงปกติไม่ใช่สัญลักษณ์ทางวิทยาศาสตร์) และ

  3. นั่นคือค่าของคีย์เมื่อ coerced กับตัวเลขคือ <= 2 ^ 32 - 2 (ซึ่งคือ 4,294,967,294) หมายเลขนั้นมาจากไหน มันเป็นส่วนหนึ่งของความหมายของดัชนีอาร์เรย์ในสเปค ตัวเลขอื่น ๆ (ไม่ใช่ตัวเลขจำนวนเต็มจำนวนลบมากกว่า 2 ^ 32 - 2) ไม่ใช่ดัชนีอาร์เรย์ เหตุผลที่เป็น 2 ^ 32 - 2คือทำให้ค่าดัชนีมากที่สุดหนึ่งต่ำกว่า 2 ^ 32 - 1ซึ่งเป็นค่าสูงสุดที่อาร์เรย์lengthสามารถมีได้ (เช่นความยาวของอาเรย์นั้นพอดีกับจำนวนเต็ม 32 บิตที่ไม่ได้ลงชื่อ) (อุปกรณ์ประกอบฉากของ RobG เพื่อชี้ให้เห็นในความคิดเห็นในโพสต์บล็อกของฉันว่าการทดสอบก่อนหน้าของฉันไม่ถูกต้อง)

คุณจะไม่ทำอย่างนั้นในรหัสแบบอินไลน์แน่นอน คุณต้องการเขียนฟังก์ชันยูทิลิตี้ บางที:

4. ใช้for-of(ใช้ตัววนซ้ำโดยนัย) (ES2015 +)

ES2015 เพิ่มตัววนซ้ำใน JavaScript วิธีที่ง่ายที่สุดในการใช้ตัววนซ้ำคือfor-ofคำสั่งใหม่ ดูเหมือนว่านี้:

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

ภายใต้หน้าปกที่ได้รับตัววนซ้ำจากอาร์เรย์และวนซ้ำผ่านมันรับค่าจากมัน นี่ไม่มีปัญหาที่ใช้for-inมีเนื่องจากจะใช้ตัววนซ้ำที่กำหนดโดยวัตถุ (อาร์เรย์) และอาร์เรย์กำหนดว่าตัววนซ้ำของพวกเขาวนซ้ำผ่านรายการ (ไม่ใช่คุณสมบัติ) ซึ่งแตกต่างจากfor-inใน ES5 ลำดับที่มีการเยี่ยมชมรายการเป็นลำดับตัวเลขของดัชนีของพวกเขา

5. ใช้ตัววนซ้ำอย่างชัดเจน (ES2015 +)

บางครั้งคุณอาจต้องการที่จะใช้iterator อย่างชัดเจน คุณสามารถทำได้เช่นกันแม้ว่ามันจะเป็น clunkier for-ofกว่ามาก ดูเหมือนว่านี้:

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

iterator เป็นวัตถุที่ตรงกับนิยามของ Iterator ในสเปค nextวิธีการของมันจะส่งคืนวัตถุผลลัพธ์ใหม่ทุกครั้งที่คุณเรียกมัน วัตถุผลลัพธ์มีคุณสมบัติdoneบอกเราว่ามันทำเสร็จแล้วและคุณสมบัติที่valueมีค่าสำหรับการทำซ้ำนั้น ( doneเป็นตัวเลือกว่ามันจะเป็นfalse, valueเป็นตัวเลือกว่ามันจะเป็นundefined.)

ความหมายของการvalueแตกต่างกันไปขึ้นอยู่กับตัววนซ้ำ; สนับสนุนอาร์เรย์ (อย่างน้อย) สามฟังก์ชันที่ส่งคืนตัววนซ้ำ:

  • values(): นี่คือสิ่งที่ฉันใช้ข้างต้น มันกลับ iterator ที่แต่ละvalueคือรายการอาร์เรย์สำหรับการย้ำว่า ( "a", "b"และ"c"ในตัวอย่างก่อนหน้านี้)
  • keys(): กลับ iterator ที่แต่ละvalueเป็นกุญแจสำคัญสำหรับการย้ำว่า (ดังนั้นสำหรับเราaข้างต้นว่าจะ"0"แล้ว"1"แล้ว"2")
  • entries(): ส่งคืนตัววนซ้ำที่แต่ละvalueอาร์เรย์เป็นแบบฟอร์ม[key, value]สำหรับการทำซ้ำนั้น

สำหรับวัตถุที่เหมือนอาร์เรย์

นอกเหนือจากอาร์เรย์ที่แท้จริงแล้วยังมีวัตถุที่เหมือนอาร์เรย์ที่มีlengthคุณสมบัติและคุณสมบัติที่มีชื่อที่เป็นตัวเลขNodeListเช่นอินสแตนซ์argumentsวัตถุ ฯลฯ เราจะวนรอบเนื้อหาได้อย่างไร

ใช้ตัวเลือกใด ๆ ด้านบนสำหรับอาร์เรย์

อย่างน้อยที่สุด, และอาจเป็นไปได้หรือมากที่สุด, ของอาเรย์ข้างต้นมักใช้กับวัตถุที่มีลักษณะคล้ายอาเรย์อย่างเท่าเทียมกัน:

  1. การใช้งานforEachและที่เกี่ยวข้อง (ES5 +)

    ฟังก์ชั่นต่างๆบนArray.prototypeคือ "เจตนาทั่วไป" และมักจะสามารถนำมาใช้ในอาร์เรย์เหมือนวัตถุผ่านหรือFunction#call Function#apply(ดูCaveat สำหรับวัตถุที่โฮสต์ให้ในตอนท้ายของคำตอบนี้ แต่มันเป็นปัญหาที่หายาก)

    สมมติว่าคุณต้องการที่จะใช้forEachในNodeของchildNodesสถานที่ให้บริการ คุณต้องการทำสิ่งนี้:

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

    หากคุณจะทำสิ่งนั้นมากคุณอาจต้องการคัดลอกการอ้างอิงฟังก์ชันลงในตัวแปรเพื่อนำมาใช้ซ้ำเช่น:

    // (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. ใช้การforวนรอบง่าย ๆ

    เห็นได้ชัดว่าการforวนซ้ำอย่างง่ายนำไปใช้กับวัตถุที่เหมือนอาร์เรย์

  3. ใช้for-in อย่างถูกต้อง

    for-inด้วยมาตรการป้องกันเช่นเดียวกับอาร์เรย์ควรทำงานกับวัตถุที่เหมือนอาร์เรย์เช่นกัน ข้อแม้สำหรับวัตถุที่โฮสต์ให้ใน # 1 ข้างต้นอาจนำไปใช้

  4. ใช้for-of(ใช้ตัววนซ้ำโดยนัย) (ES2015 +)

    for-ofใช้ตัววนซ้ำที่จัดเตรียมโดยวัตถุ (ถ้ามี) ซึ่งรวมถึงวัตถุที่โฮสต์ให้ ตัวอย่างเช่นข้อมูลจำเพาะสำหรับNodeListจากquerySelectorAllได้รับการอัปเดตเพื่อรองรับการทำซ้ำ สเป็คสำหรับHTMLCollectionจากgetElementsByTagNameไม่ได้

  5. ใช้ตัววนซ้ำอย่างชัดเจน (ES2015 +)

    ดู # 4

สร้างอาร์เรย์ที่แท้จริง

ในบางครั้งคุณอาจต้องการแปลงวัตถุคล้ายอาร์เรย์ให้เป็นอาร์เรย์จริง การทำเช่นนั้นเป็นเรื่องง่ายอย่างน่าประหลาดใจ:

  1. ใช้sliceวิธีการของอาร์เรย์

    เราสามารถใช้sliceวิธีการของอาร์เรย์ซึ่งเช่นเดียวกับวิธีอื่น ๆ ที่กล่าวถึงข้างต้นคือ "จงใจทั่วไป" และสามารถใช้กับวัตถุที่มีลักษณะคล้ายอาร์เรย์ได้ดังนี้:

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

    ตัวอย่างเช่นถ้าเราต้องการแปลง a NodeListเป็นอาร์เรย์ที่แท้จริงเราสามารถทำได้:

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

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

  2. ใช้ไวยากรณ์การแพร่กระจาย ( ...)

    นอกจากนี้ยังเป็นไปได้ที่จะใช้ไวยากรณ์การแพร่กระจายของ ES2015 กับเครื่องมือ JavaScript ที่สนับสนุนคุณสมบัตินี้ เช่นfor-ofนี้ใช้ตัววนซ้ำโดยวัตถุ (ดู # 4 ในส่วนก่อนหน้า):

    var trueArray = [...iterableObject];

    ตัวอย่างเช่นถ้าเราต้องการแปลงเป็นNodeListอาร์เรย์จริงด้วยไวยากรณ์การแพร่กระจายนี้จะค่อนข้างรวบรัด:

    var divs = [...document.querySelectorAll("div")];
  3. ใช้ Array.from

    Array.from (spec) | (MDN) (ES2015 + แต่เป็นโพลีฟิลอย่างง่ายดาย) สร้างอาร์เรย์จากวัตถุที่มีลักษณะคล้ายอาร์เรย์เลือกที่จะส่งผ่านรายการผ่านฟังก์ชันการแมปก่อน ดังนั้น:

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

    หรือถ้าคุณต้องการที่จะได้รับอาร์เรย์ของชื่อแท็กขององค์ประกอบที่มีชั้นเรียนที่คุณต้องการใช้ฟังก์ชั่นการทำแผนที่:

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

ข้อแม้สำหรับวัตถุที่โฮสต์ให้

หากคุณใช้Array.prototypeฟังก์ชั่นที่มีวัตถุคล้ายอาร์เรย์ที่โฮสต์ให้บริการ (รายการ DOM และสิ่งอื่น ๆ ที่ให้บริการโดยเบราว์เซอร์แทนที่จะเป็นเครื่องมือ JavaScript) คุณต้องแน่ใจว่าได้ทดสอบในสภาพแวดล้อมเป้าหมายของคุณเพื่อให้แน่ใจว่าวัตถุที่โฮสต์ให้ทำงาน . ส่วนใหญ่ทำงานได้อย่างถูกต้อง (ตอนนี้) แต่สิ่งสำคัญคือการทดสอบ เหตุผลก็คือArray.prototypeวิธีการส่วนใหญ่ที่คุณต้องการใช้นั้นขึ้นอยู่กับออบเจ็กต์ที่โฮสต์จัดเตรียมไว้ให้ซึ่งเป็นคำตอบที่ถูกต้องสำหรับการ[[HasProperty]]ดำเนินการเชิงนามธรรม จากการเขียนนี้เบราว์เซอร์ทำงานได้ดีมากในเรื่องนี้ แต่ข้อมูลจำเพาะ 5.1 อนุญาตให้มีความเป็นไปได้ที่วัตถุที่โฮสต์จัดหาอาจไม่ตรงไปตรงมา มันอยู่ใน§8.6.2หลายย่อหน้าด้านล่างของตารางขนาดใหญ่ใกล้กับจุดเริ่มต้นของส่วนนั้น) ซึ่งมันบอกว่า:

วัตถุโฮสต์อาจใช้วิธีการภายในเหล่านี้ในลักษณะใด ๆ เว้นแต่จะระบุไว้เป็นอย่างอื่น ตัวอย่างเช่นความเป็นไปได้อย่างหนึ่งคือ[[Get]]และ[[Put]]สำหรับวัตถุโฮสต์ที่แน่นอนเรียกและเก็บค่าคุณสมบัติ แต่[[HasProperty]]สร้างเท็จเสมอ

(ผมไม่สามารถหาคำฟุ่มเฟือยเทียบเท่าในสเป็ค ES2015 แต่ก็ผูกพันกับยังคงเป็นกรณี.) อีกครั้งขณะที่เขียนนี้ร่วมกันเป็นเจ้าภาพที่ให้อาร์เรย์เหมือนวัตถุในเบราว์เซอร์ที่ทันสมัย [ NodeListกรณีตัวอย่าง] ทำด้าม[[HasProperty]]ถูกต้อง แต่สิ่งสำคัญคือการทดสอบ)


44
ฉันต้องการเพิ่มที่.forEachไม่สามารถแยกได้อย่างมีประสิทธิภาพ คุณต้องโยนข้อยกเว้นเพื่อทำการหยุดพัก
Pijusn

82
@Pius: someถ้าคุณต้องการที่จะทำลายห่วงคุณสามารถใช้ (ฉันอยากจะอนุญาตให้ทำลายforEachด้วยเช่นกัน แต่พวกเขาอืมไม่ได้ถามฉัน ;-))
TJ Crowder

6
@TJCrowder True แม้ว่าจะดูเหมือนการทำงานมากกว่าเพราะไม่ใช่จุดประสงค์หลัก
Pijusn

8
@ user889030: คุณจำเป็นต้องมี,หลังจากที่ไม่ได้เป็นk=0 ;โปรดจำไว้ว่าการเขียนโปรแกรมมีหลายสิ่งหลายอย่างหนึ่งในนั้นคือใส่ใจในรายละเอียด ... :-)
TJ Crowder

5
@JimB: ที่กล่าวถึงข้างต้น (และlengthไม่ใช่วิธี) :-)
TJ Crowder

513

หมายเหตุ : คำตอบนี้ล้าสมัยอย่างสิ้นหวัง สำหรับวิธีการที่ทันสมัยมากขึ้นดูที่วิธีการที่มีอยู่บนอาร์เรย์ วิธีการที่น่าสนใจอาจเป็น:

  • แต่ละ
  • แผนที่
  • กรอง
  • ซิป
  • ลด
  • ทุกๆ
  • บาง

วิธีมาตรฐานในการย้ำอาร์เรย์ในJavaScriptคือ vanilla for-loop:

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

อย่างไรก็ตามโปรดทราบว่าวิธีการนี้ดีเฉพาะถ้าคุณมีอาร์เรย์ที่หนาแน่นและแต่ละดัชนีจะถูกครอบครองโดยองค์ประกอบ หากอาร์เรย์จะเบาบางแล้วคุณสามารถใช้เป็นปัญหาประสิทธิภาพการทำงานด้วยวิธีนี้เนื่องจากคุณจะย้ำกว่ามากของดัชนีที่ไม่ได้จริงๆอยู่ในอาร์เรย์ ในกรณีนี้ a for .. in-loop อาจเป็นแนวคิดที่ดีกว่า อย่างไรก็ตามคุณต้องใช้การป้องกันที่เหมาะสมเพื่อให้แน่ใจว่ามีการดำเนินการเฉพาะคุณสมบัติที่ต้องการของอาเรย์ (นั่นคือองค์ประกอบของอาเรย์) เนื่องจากfor..in-loop จะถูกระบุในเบราว์เซอร์ดั้งเดิมหรือหากคุณสมบัติเพิ่มเติมถูกกำหนดเป็นenumerable.

ในECMAScript 5จะมีวิธี forEach ของอาเรย์ต้นแบบ แต่ไม่รองรับในเบราว์เซอร์รุ่นเก่า ดังนั้นเพื่อให้สามารถใช้งานได้อย่างสม่ำเสมอคุณต้องมีสภาพแวดล้อมที่รองรับ (เช่นNode.jsสำหรับ JavaScript ฝั่งเซิร์ฟเวอร์) หรือใช้ "Polyfill" Polyfill สำหรับฟังก์ชั่นนี้มีความสำคัญและทำให้อ่านรหัสได้ง่ายขึ้นจึงเป็นโพลีฟิลที่ดีที่จะรวม


2
ทำไมfor(instance in objArray) การใช้งานไม่ถูกต้อง มันดูเรียบง่ายสำหรับฉัน แต่ฉันได้ยินว่าคุณพูดถึงวิธีการใช้งานที่ไม่ถูกต้อง?
Dante1986

21
คุณสามารถใช้การแคชความยาวแบบอินไลน์: สำหรับ (var i = 0, l = arr.length; i <l; i ++)
Robert Hoffmann

3
เครื่องหมายจุลภาคที่จุดสิ้นสุดของบรรทัดแรกโดยเจตนาหรือเป็นพิมพ์ผิด (อาจเป็นอัฒภาค)?
Mohd Abdul Mujib

6
@ wardha-Web มันเป็นความตั้งใจ มันช่วยให้เราสามารถประกาศตัวแปรหลายตัวด้วยvarคำหลักเดียว ถ้าเราใช้เซมิโคลอนก็elementจะถูกประกาศในขอบเขตทั่วโลก (หรือค่อนข้าง JSHint จะกรีดร้องที่เราก่อนที่จะถึงการผลิต)
PatrikAkerstrand

239

หากคุณใช้ไลบรารีjQueryคุณสามารถใช้jQuery.each :

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

แก้ไข:

ตามคำถามผู้ใช้ต้องการรหัสใน javascript แทน jquery ดังนั้นการแก้ไขคือ

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

2
ฉันอาจจะใช้คำตอบนี้บ่อยที่สุด มันไม่ใช่คำตอบที่ดีที่สุดสำหรับคำถาม แต่ในทางปฏิบัติจะเป็นวิธีที่ง่ายที่สุดและเหมาะสมที่สุดสำหรับพวกเราที่ใช้ jQuery ฉันคิดว่าเราทุกคนควรเรียนรู้วิธีวานิลลาด้วย ไม่เคยเจ็บปวดที่จะขยายความเข้าใจของคุณ
mopsyd

47
เพียงเพื่อประโยชน์ของมัน: jQuery แต่ละคำตอบนั้นช้ากว่ามาก แนะนำโดย jQuery ให้ใช้ JavaScript ดั้งเดิมแทน jQuery เมื่อเป็นไปได้ jsperf.com/browser-diet-jquery-each-vs-for-loop
Kevin Boss

8
หลีกเลี่ยงการใช้ jQuery เมื่อคุณสามารถใช้วานิลลา js
Noe

2
ติดกับ JS มาตรฐาน
อย่าให้ไลบรารี่

116

วนไปข้างหลัง

ฉันคิดว่าการย้อนกลับของลูปควรได้รับการกล่าวถึงที่นี่:

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

ข้อดี:

  • คุณไม่จำเป็นต้องประกาศlenตัวแปรชั่วคราวหรือเปรียบเทียบกับarray.lengthการทำซ้ำแต่ละครั้งซึ่งหนึ่งในนั้นอาจเป็นการปรับให้เหมาะสมหนึ่งนาที
  • ถอดพี่น้องจาก DOM ในการสั่งซื้อกลับเป็นมักจะมีประสิทธิภาพมากขึ้น (เบราว์เซอร์ต้องเปลี่ยนองค์ประกอบน้อยลงในอาร์เรย์ภายใน)
  • หากคุณปรับเปลี่ยนอาร์เรย์ในขณะที่วนรอบที่หรือหลังจากดัชนีi (ตัวอย่างเช่นคุณลบหรือแทรกรายการที่array[i]) จากนั้นวนไปข้างหน้าจะข้ามรายการที่เลื่อนไปทางซ้ายสู่ตำแหน่งiหรือดำเนินการรายการiที่เป็น เลื่อนไปทางขวา ในแบบดั้งเดิมสำหรับลูปคุณสามารถอัปเดตiให้ชี้ไปที่รายการถัดไปที่ต้องการการประมวลผล - 1 แต่การกลับทิศทางของการวนซ้ำนั้นมักจะเป็นวิธีที่ง่ายกว่าและสวยงามกว่า
  • ในทำนองเดียวกันเมื่อแก้ไขหรือลบองค์ประกอบ DOM ที่ซ้อนกันการประมวลผลในสิ่งที่ตรงกันข้ามสามารถหลีกเลี่ยงข้อผิดพลาดได้ ตัวอย่างเช่นพิจารณาการแก้ไข innerHTML ของโหนดพาเรนต์ก่อนจัดการกับชายด์ ตามเวลาที่โหนดลูกมาถึงมันจะถูกลบออกจาก DOM, ถูกแทนที่ด้วยเด็กที่สร้างขึ้นใหม่เมื่อ InnerHTML ของผู้ปกครองถูกเขียน
  • มันสั้นกว่าในการพิมพ์และอ่านกว่าตัวเลือกอื่น ๆ แม้ว่ามันจะสูญเสียไปforEach()และเพื่อ for ... ofES6

ข้อเสีย:

  • มันประมวลผลรายการในลำดับย้อนกลับ หากคุณกำลังสร้างอาร์เรย์ใหม่จากผลลัพธ์หรือพิมพ์สิ่งต่าง ๆ บนหน้าจอโดยธรรมชาติผลลัพธ์จะถูกย้อนกลับตามลำดับดั้งเดิม
  • ซ้ำแล้วซ้ำอีกแทรกเข้าไปใน DOM พี่น้องเป็นลูกคนแรกเพื่อที่จะรักษาคำสั่งของพวกเขาคือมีประสิทธิภาพน้อยกว่า (เบราว์เซอร์จะต้องเปลี่ยนสิ่งต่าง ๆ อย่างถูกต้อง) ในการสร้างโหนด DOM อย่างมีประสิทธิภาพและตามลำดับเพียงวนไปข้างหน้าและต่อท้ายตามปกติ (และใช้ "ชิ้นส่วนเอกสาร")
  • วนกลับจะสับสนกับนักพัฒนาจูเนียร์ (คุณอาจพิจารณาว่าเป็นข้อได้เปรียบขึ้นอยู่กับแนวโน้มของคุณ)

ฉันควรใช้มันตลอดเวลาหรือไม่?

นักพัฒนาบางคนใช้การย้อนกลับสำหรับการวนซ้ำโดยค่าเริ่มต้นเว้นแต่จะมีเหตุผลที่ดีที่จะส่งต่อการวนซ้ำ

แม้ว่าประสิทธิภาพจะเพิ่มขึ้นเล็กน้อย แต่ก็มีเสียงกรีดร้อง:

"ทำสิ่งนี้กับทุกรายการในรายการฉันไม่สนใจเกี่ยวกับการสั่งซื้อ!"

อย่างไรก็ตามในทางปฏิบัติที่เป็นไม่จริงเป็นตัวบ่งชี้ความน่าเชื่อถือของเจตนาเพราะมันเป็นแยกไม่ออกจากโอกาสเหล่านั้นเมื่อคุณทำดูแลเกี่ยวกับการสั่งซื้อและทำจริงๆจำเป็นต้องห่วงในสิ่งที่ตรงกันข้าม ดังนั้นในความเป็นจริงสร้างอีกจะต้องถูกต้องแสดงว่า "ไม่สนใจ" เจตนาอะไรบางอย่างที่ไม่สามารถใช้งานในภาษามากที่สุดรวมทั้ง ECMAScript ปัจจุบัน forEachUnordered()แต่ที่อาจจะเรียกว่ายกตัวอย่างเช่น

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

มันเป็นการดีที่จะใช้ forEach ()

โดยทั่วไปแล้วสำหรับรหัสระดับที่สูงขึ้นซึ่งความชัดเจนและความปลอดภัยเป็นเรื่องที่น่ากังวลมากขึ้นผมขอแนะนำให้ใช้Array::forEachเป็นรูปแบบเริ่มต้นของคุณในการวนลูป (แม้ว่าวันนี้ฉันชอบที่จะใช้for..of) เหตุผลที่ชอบforEachมากกว่าการวนกลับคือ:

  • อ่านง่ายกว่า
  • มันแสดงให้เห็นว่าฉันจะไม่ได้รับการเลื่อนภายในบล็อก (ซึ่งมักจะเป็นที่หลบซ่อนตัวประหลาดใจเป็นไปได้ในระยะยาวforและwhileลูป)
  • มันให้ขอบเขตฟรีสำหรับการปิด
  • จะช่วยลดการรั่วไหลของตัวแปรท้องถิ่นและการชนกันโดยไม่ตั้งใจกับ (และการกลายพันธุ์ของ) ตัวแปรภายนอก

จากนั้นเมื่อคุณเห็นการย้อนกลับของการวนซ้ำในรหัสของคุณนั่นเป็นคำใบ้ว่ามันถูกย้อนกลับด้วยเหตุผลที่ดี (อาจเป็นหนึ่งในเหตุผลที่อธิบายไว้ข้างต้น) และการมองไปข้างหน้าแบบดั้งเดิมสำหรับลูปอาจบ่งบอกว่าสามารถขยับได้

(หากการอภิปรายเกี่ยวกับเจตนาไม่สมเหตุสมผลกับคุณคุณและรหัสของคุณอาจได้รับประโยชน์จากการดูการบรรยายของ Crockford เกี่ยวกับรูปแบบการเขียนโปรแกรมและสมองของคุณ )

ตอนนี้มันดีกว่าที่จะใช้สำหรับ .. ของ!

มีการถกเถียงกันว่าจะดีกว่าfor..ofหรือไม่forEach():

  • สำหรับการรองรับเบราว์เซอร์สูงสุดfor..of ต้องใช้ตัวเติมสำหรับตัววนซ้ำทำให้แอปของคุณช้าลงเล็กน้อยในการดำเนินการและดาวน์โหลดให้ใหญ่ขึ้นเล็กน้อย

  • ด้วยเหตุผลนั้น (และเพื่อส่งเสริมการใช้mapและfilter) คำแนะนำแบบฟรอนต์เอนด์บางคำสั่งห้ามfor..ofอย่างสมบูรณ์!

  • แต่ข้อกังวลข้างต้นไม่สามารถใช้ได้กับแอปพลิเคชัน Node.js ซึ่งfor..ofขณะนี้ได้รับการสนับสนุนเป็นอย่างดี

  • และยิ่งawait ไม่ทำงานforEach()ภายใน การใช้for..ofเป็นรูปแบบที่ชัดเจนที่สุดในกรณีนี้

โดยส่วนตัวแล้วฉันมักจะใช้สิ่งที่อ่านง่ายที่สุดเว้นแต่ว่าประสิทธิภาพหรือการย่อจะกลายเป็นปัญหาใหญ่ ดังนั้นวันนี้ผมชอบที่จะใช้for..ofแทนforEach()แต่ฉันมักจะใช้mapหรือfilterหรือfindหรือsomeใช้บังคับเมื่อ (เพื่อประโยชน์ของเพื่อนร่วมงานของฉันฉันไม่ค่อยได้ใช้reduce)


มันทำงานยังไง?

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

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

คุณจะสังเกตเห็นว่าi--เป็นประโยคกลาง (ที่เรามักจะเห็นการเปรียบเทียบ) และประโยคสุดท้ายว่างเปล่า (ที่เรามักจะเห็นi++) นั่นหมายความว่าi--จะใช้เป็นเงื่อนไขสำหรับการต่อเนื่อง สิ่งสำคัญคือมันจะถูกดำเนินการและตรวจสอบก่อนการทำซ้ำแต่ละครั้ง

  • มันจะเริ่มarray.lengthได้อย่างไรโดยไม่ระเบิด

    เพราะi--วิ่งก่อนที่จะซ้ำกันในการย้ำแรกที่เราจะจริงจะเข้าถึงรายการที่array.length - 1ที่หลีกเลี่ยงปัญหาใด ๆ กับอาร์เรย์ออกจากขอบเขต undefinedรายการ

  • ทำไมมันไม่หยุดย้ำก่อนดัชนี 0?

    ลูปจะหยุดการวนซ้ำเมื่อเงื่อนไขi--ประเมินค่า falsey (เมื่อให้ผลเป็น 0)

    เคล็ดลับคือไม่เหมือน--iกันตัวi--ดำเนินการต่อท้ายจะลดลงiแต่ให้ค่าก่อนที่จะลดลง คอนโซลของคุณสามารถสาธิตสิ่งนี้:

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

    [5, 5, 4]

    ดังนั้นในการทำซ้ำครั้งสุดท้ายฉันเป็น1ก่อนหน้านี้และการi--แสดงออกเปลี่ยนเป็น0แต่จริง ๆ แล้วให้ผลตอบแทน1 (ความจริง) และเงื่อนไขผ่าน ทวนถัดไปi--เปลี่ยนแปลงฉันจะ-1แต่อัตราผลตอบแทน0 (falsey) ทำให้เกิดการดำเนินการทันทีหล่นออกมาจากด้านล่างของวง

    ในการส่งต่อแบบดั้งเดิมสำหรับวงi++และ++iสามารถใช้แทนกันได้ (ตามที่ Douglas Crockford ชี้ให้เห็น) อย่างไรก็ตามใน reverse for loop เนื่องจากการลดลงของเรายังเป็นการแสดงออกถึงเงื่อนไขของเราเราต้องยึดติดกับi--ถ้าเราต้องการประมวลผลรายการที่ดัชนี 0


เรื่องไม่สำคัญ

บางคนชอบวาดลูกศรเล็ก ๆ ในforวงย้อนกลับและจบด้วยพริบตา:

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

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


3
ฉันลืมที่จะเพิ่มมาตรฐาน ฉันลืมที่จะพูดถึงว่าการวนลูปย้อนกลับเป็นการเพิ่มประสิทธิภาพที่สำคัญของโปรเซสเซอร์ 8 บิตเช่น 6502 ซึ่งคุณจะได้รับการเปรียบเทียบจริง ๆ ฟรี!
joeytwiddle

คำตอบเดียวกันนี้จะได้รับอย่างกระชับมากขึ้นที่นี่ (ในคำถามที่แตกต่าง)
joeytwiddle

84

ภาษาC- style บางภาษาใช้foreachในการวนลูปผ่านการแจกแจง ใน JavaScript สามารถทำได้ด้วยfor..inโครงสร้างลูป :

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

มีการจับ for..inจะวนซ้ำแต่ละสมาชิกของวัตถุที่นับจำนวนได้และสมาชิกบนต้นแบบของมัน เพื่อหลีกเลี่ยงการอ่านค่าที่สืบทอดมาจากต้นแบบของวัตถุเพียงตรวจสอบว่าทรัพย์สินเป็นของวัตถุ:

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

นอกจากนี้ECMAScript 5ได้เพิ่มforEachวิธีการArray.prototypeที่สามารถใช้ในการแจกแจงอาร์เรย์โดยใช้ calback (polyfill อยู่ในเอกสารเพื่อให้คุณยังสามารถใช้สำหรับเบราว์เซอร์รุ่นเก่า):

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

สิ่งสำคัญคือให้ทราบว่าArray.prototype.forEachจะไม่แตกเมื่อโทรกลับfalseมา jQueryและUnderscore.jsจัดเตรียมรูปแบบของตนเองeachเพื่อให้ลูปที่สามารถลัดวงจรได้


3
ดังนั้นวิธีหนึ่งจะแยกออกจาก aa ECMAScript5 foreach loop เหมือนที่เราต้องการสำหรับ loop หรือ foreach loop เหมือนที่พบในภาษา C-style?
Ciaran Gallagher

5
@CiaranG ใน JavaScript เป็นเรื่องปกติที่จะเห็นeachวิธีการที่อนุญาตให้return falseใช้เพื่อแยกวงออก แต่ด้วยforEachตัวเลือกนี้ไม่ใช่ตัวเลือก สามารถใช้ค่าสถานะภายนอกได้ (เช่นif (flag) return;แต่จะป้องกันเฉพาะส่วนที่เหลือของฟังก์ชันฟังก์ชันเท่านั้นที่forEachจะดำเนินการต่อไป แต่จะยังคงวนซ้ำทั้งชุดทั้งหมดต่อไป
zzzzBov

43

หากคุณต้องการวนซ้ำอาร์เรย์ให้ใช้วงสามส่วนforมาตรฐาน

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

คุณสามารถรับประสิทธิภาพการเพิ่มประสิทธิภาพได้โดยการแคชmyArray.lengthหรือวนซ้ำไปข้างหลัง


6
สำหรับ (var i = 0, length = myArray.length; i <length; i ++) ควรทำ
Edson Medina

5
@EdsonMedina lengthที่ยังจะสร้างตัวแปรทั่วโลกใหม่ที่เรียกว่า ;)
joeytwiddle

4
@joeytwiddle ใช่ แต่นั่นไม่เกินขอบเขตของโพสต์นี้ คุณจะสร้างตัวแปรทั่วโลกฉันต่อไป
Edson Medina

6
@ EdsonMedina คำขอโทษของฉันฉันผิดอย่างนั้น ใช้,หลังจากที่มอบหมายไม่ได้แนะนำโลกใหม่เพื่อให้ข้อเสนอแนะของคุณเป็นเพียงที่ดี ! ฉันสับสนสิ่งนี้สำหรับปัญหาที่แตกต่าง: การใช้=หลังจากที่ได้รับมอบหมายจะสร้างโลกใหม่
joeytwiddle

ระวังตัวแปร i ไม่ใช่โลคอลของลูป JavaScript ไม่มีขอบเขตบล็อก มันอาจจะดีกว่าที่จะประกาศvar i, length, arrayItem;ก่อนวงเพื่อหลีกเลี่ยงความเข้าใจผิดนี้
James

35

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

angular.forEachใช้เวลา 2 ข้อโต้แย้งและข้อโต้แย้งที่สามตัวเลือก อาร์กิวเมนต์แรกคือวัตถุ (อาร์เรย์) เพื่อวนซ้ำอาร์กิวเมนต์ที่สองคือฟังก์ชันตัววนซ้ำและอาร์กิวเมนต์ที่สามที่เป็นตัวเลือกคือบริบทของวัตถุ

มีวิธีการที่แตกต่างกันในการใช้ forEach แต่ละวงของมุม ที่ง่ายที่สุดและอาจใช้มากที่สุดคือ

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

อีกวิธีหนึ่งที่มีประโยชน์สำหรับการคัดลอกไอเท็มจากหนึ่งอาเรย์หนึ่งไปอีกอันคือ

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

แม้ว่าคุณไม่จำเป็นต้องทำเช่นนั้นคุณสามารถทำสิ่งต่อไปนี้และเทียบเท่ากับตัวอย่างก่อนหน้านี้:

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

ขณะนี้มีข้อดีและข้อเสียของการใช้angular.forEachฟังก์ชั่นซึ่งตรงข้ามกับวงวนิลาที่ปรุงแต่งforแล้ว

ข้อดี

  • อ่านง่าย
  • เขียนได้ง่าย
  • หากมีangular.forEachจะใช้ ES5 forEach loop ตอนนี้ผมจะได้รับจากการทดลองในส่วนข้อเสียเป็นลูป forEach มีมากช้ากว่าสำหรับลูป ฉันพูดถึงเรื่องนี้ในฐานะมืออาชีพเพราะมันดีที่จะสอดคล้องและเป็นมาตรฐาน

พิจารณาลูปซ้อนกัน 2 ต่อไปนี้ซึ่งทำสิ่งเดียวกัน สมมติว่าเรามี 2 อาร์เรย์ของวัตถุและแต่ละวัตถุมีอาร์เรย์ของผลลัพธ์แต่ละอันมีคุณสมบัติค่าที่เป็นสตริง (หรืออะไรก็ตาม) และสมมติว่าเราจำเป็นต้องทำซ้ำผลลัพธ์แต่ละรายการและหากพวกมันเท่ากันให้ดำเนินการบางอย่าง:

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

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

จุดด้อย

  • อย่างมีประสิทธิภาพ angular.forEachและพื้นเมืองforEachสำหรับเรื่องที่มีทั้งมากช้ากว่าปกติforห่วง .... เกี่ยวกับ90% ช้า ดังนั้นสำหรับชุดข้อมูลขนาดใหญ่ควรยึดติดกับการforวนซ้ำดั้งเดิม
  • ไม่หยุดพักดำเนินการต่อหรือส่งคืนการสนับสนุน continueได้รับการสนับสนุนจริงโดย " อุบัติเหตุ " เพื่อดำเนินการต่อในการที่angular.forEachคุณง่ายใส่return;คำสั่งในฟังก์ชั่นangular.forEach(array, function(item) { if (someConditionIsTrue) return; });ที่จะทำให้มันออกไปจากฟังก์ชั่นสำหรับการทำซ้ำนั้น นี่ก็เป็นเพราะความจริงที่ว่าชาวพื้นเมืองforEachไม่สนับสนุนการหยุดพักหรือดำเนินการต่อเช่นกัน

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


34

หากคุณไม่สนใจล้างอาเรย์

var x;

while(x = y.pop()){ 

    alert(x); //do something 

}

xจะมีค่าสุดท้ายของyและมันจะถูกลบออกจากอาร์เรย์ นอกจากนี้คุณยังสามารถใช้ที่จะให้และลบรายการแรกจากshift()y


4
[1, 2, undefined, 3]มันไม่ทำงานหากเกิดขึ้นจะมีอาร์เรย์ที่เบาบางเช่น
M. Grzywaczewski

2
... หรือสิ่งใดเท็จ: [1, 2, 0, 3]หรือ[true, true, false, true]
joeytwiddle

31

forEachการดำเนินงาน ( เห็นใน 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
ตัววนซ้ำในสิ่งนี้กำลังทำการคำนวณความยาวที่ไม่จำเป็น ในกรณีที่เหมาะสมที่สุดความยาวของรายการควรคำนวณเพียงครั้งเดียว
MIdhun Krishna

2
@MIdhunKrishna ฉันอัปเดตคำตอบและ jsFiddle ของฉันแล้ว แต่โปรดระวังว่ามันไม่ง่ายอย่างที่คุณคิด ตรวจสอบคำถาม
nmoliveira

2
การใช้งานที่สมบูรณ์และถูกต้องพบได้ที่นี่: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
marciowb

29

อาจเป็นfor(i = 0; i < array.length; i++)ลูปไม่ใช่ตัวเลือกที่ดีที่สุด ทำไม? หากคุณมีสิ่งนี้:

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

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

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

และถ้าคุณต้องการให้มันเป็นฟังก์ชั่นคุณสามารถทำได้:

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

ถ้าคุณต้องการที่จะทำลายตรรกะน้อยมาก:

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

ตัวอย่าง:

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

มันกลับมา:

//Hello
//World
//!!!

29

มีการนำไปใช้งานสามแบบforeachในjQueryดังนี้

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
บันทึกคอนโซลเป็นเพียงการสาธิต นั่นคือการทำให้มันเป็นตัวอย่างการทำงาน cmplete
Rajesh Paul

29

ณ วันที่ ECMAScript 6:

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

ในกรณีที่ofหลีกเลี่ยงความแปลกประหลาดที่เกี่ยวข้องกับinและทำให้มันทำงานเหมือนforวนของภาษาอื่น ๆ และletผูกiภายในวงเมื่อเทียบกับภายในฟังก์ชั่น

วงเล็บปีกกา ( {}) สามารถละเว้นได้เมื่อมีเพียงหนึ่งคำสั่ง (เช่นในตัวอย่างด้านบน)


28

วิธีง่ายในตอนนี้จะเป็นการใช้ห้องสมุด underscore.js มันมีเครื่องมือที่มีประโยชน์มากมายเช่นeachและจะมอบหมายงานให้กับเจ้าของภาษาโดยอัตโนมัติforEachหากมี

ตัวอย่างของCodePenว่าทำงานอย่างไร:

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

ดูสิ่งนี้ด้วย


23

มีไม่ใด ๆfor eachวงในพื้นเมืองJavaScript คุณสามารถใช้ไลบรารีเพื่อรับฟังก์ชั่นนี้ (ฉันแนะนำUnderscore.js ) ใช้การforวนรอบแบบง่าย ๆ

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

อย่างไรก็ตามโปรดทราบว่าอาจมีเหตุผลที่จะใช้การforวนซ้ำที่ง่ายกว่าเดิม(ดูคำถาม Stack Overflow เหตุใดการใช้“ for … in” กับการวนซ้ำของอาร์เรย์จึงเป็นความคิดที่ไม่ดี? )

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

22

นี่เป็นตัววนซ้ำสำหรับรายการที่ไม่กระจายซึ่งดัชนีเริ่มต้นที่ 0 ซึ่งเป็นสถานการณ์ทั่วไปเมื่อจัดการกับ document.getElementsByTagName หรือ document.querySelectorAll)

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;  

ตัวอย่างการใช้งาน:

ตัวอย่างที่ 1

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

ตัวอย่างที่ 2

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

แต่ละแท็กจะได้รับ class="blue"

ตัวอย่างที่ 3

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

แท็ก p อื่น ๆ จะได้รับclass="red">

ตัวอย่าง # 4

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

และในที่สุดแท็กสีน้ำเงิน 20 อันแรกก็เปลี่ยนเป็นสีเขียว

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


21

มีวิธีการวนซ้ำในอาร์เรย์ใน JavaScript ดังนี้:

สำหรับ - เป็นหนึ่งที่พบมากที่สุด รหัสเต็มบล็อกสำหรับการวนซ้ำ

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>

ในขณะที่ - วงในขณะที่เงื่อนไขผ่าน ดูเหมือนว่าจะเป็นวงที่เร็วที่สุด

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

do / while - ยังวนลูปผ่านบล็อกของรหัสในขณะที่เงื่อนไขเป็นจริงจะทำงานอย่างน้อยหนึ่งครั้ง

var text = ""
var i = 0;

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

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

ลูปการทำงาน - forEach, map, filterยังreduce(ห่วงพวกเขาผ่านการทำงาน แต่พวกเขาจะใช้ถ้าคุณต้องการที่จะทำบางสิ่งบางอย่างกับอาร์เรย์ของคุณ ฯลฯ

// 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>

สำหรับข้อมูลเพิ่มเติมและตัวอย่างเกี่ยวกับการเขียนโปรแกรมการทำงานบนอาร์เรย์ดูที่โพสต์บล็อกโปรแกรมการทำงานใน JavaScript: แผนที่กรองและลด


การแก้ไขเล็กน้อย: forEachไม่ใช่ลูป "ใช้งานได้" เนื่องจากไม่ได้ส่งคืนใหม่Array(อันที่จริงแล้วมันไม่ส่งคืนอะไรเลย) แต่มันวนซ้ำ
nicholaswmin

19

ECMAScript 5 (เวอร์ชันบน JavaScript) เพื่อทำงานกับอาร์เรย์:

forEach - วนซ้ำทุกรายการในอาร์เรย์และทำทุกอย่างที่คุณต้องการกับแต่ละรายการ

['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

ในกรณีที่มีความสนใจในการดำเนินการเกี่ยวกับอาร์เรย์โดยใช้คุณสมบัติ inbuilt

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

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

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

ลด - ตามชื่อบอกว่ามันจะลดอาร์เรย์เป็นค่าเดียวโดยการเรียกฟังก์ชั่นที่กำหนดผ่านในองค์ประกอบปัจจุบันและผลของการดำเนินการก่อนหน้านี้

[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

ทุก - ส่งกลับจริงหรือเท็จถ้าองค์ประกอบทั้งหมดในอาร์เรย์ผ่านการทดสอบในฟังก์ชั่นการโทรกลับ

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

// Output: false

ตัวกรอง - คล้ายกันมากกับทุกตัวยกเว้นตัวกรองนั้นจะส่งกลับอาร์เรย์พร้อมองค์ประกอบที่ส่งกลับค่าจริงไปยังฟังก์ชันที่กำหนด

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

// Output: [2,4,6]

16

ไม่มีความสามารถที่จะทำลาย inbuilt forEachในเป็น หากต้องการขัดจังหวะการทำงานให้ใช้สิ่งArray#someต่อไปนี้:

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

ใช้งานได้เนื่องจากsomeคืนค่าเป็นจริงทันทีที่มีการเรียกกลับใด ๆ ดำเนินการตามลำดับอาร์เรย์ส่งคืนจริงประมวลผลส่วนที่เหลือลัดวงจร คำตอบเดิม ดูที่ Array prototype สำหรับบางคน


13

ฉันยังต้องการเพิ่มสิ่งนี้เป็นองค์ประกอบของการวนกลับและคำตอบข้างต้นสำหรับคนที่ต้องการไวยากรณ์นี้ด้วย

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

ข้อดี:

ประโยชน์สำหรับสิ่งนี้: คุณมีการอ้างอิงอยู่แล้วในแบบแรกที่ไม่จำเป็นต้องประกาศในภายหลังด้วยบรรทัดอื่น มันมีประโยชน์เมื่อวนลูปอาร์เรย์อาร์เรย์วัตถุ

จุดด้อย:

สิ่งนี้จะแตกเมื่อใดก็ตามที่การอ้างอิงเป็นเท็จ - เท็จ (ไม่ได้กำหนด ฯลฯ ) มันสามารถใช้เป็นข้อได้เปรียบแม้ว่า อย่างไรก็ตามมันจะทำให้อ่านยากขึ้นเล็กน้อย และขึ้นอยู่กับเบราว์เซอร์มันสามารถ "ไม่" ปรับให้ทำงานเร็วกว่าเดิม


12

วิธีการใช้ 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
ฉันคิดว่าผลลัพธ์น่าจะเป็น[undefined, 2, undefined, 4, undefined, 6, undefined]มากกว่านี้

10

การใช้ลูปกับ ECMAScript 6 การทำลายล้างและตัวดำเนินการสเปรด

การทำลายล้างและการใช้งานของตัวดำเนินการสเปรดได้พิสูจน์แล้วว่าค่อนข้างมีประโยชน์สำหรับผู้มาใหม่ไปยัง ECMAScript 6 ว่าเป็นมนุษย์ที่อ่านได้ / สวยงามมากขึ้นแม้ว่าทหารผ่านศึกจาวาสคริปต์บางคนอาจคิดว่ามันยุ่งเหยิง รุ่นน้องหรือคนอื่น ๆ อาจพบว่ามีประโยชน์

ตัวอย่างต่อไปนี้จะใช้for...ofคำสั่งและ.forEachวิธีการ

ตัวอย่าง 6, 7, 8 และสามารถใช้กับลูปการทำงานใด ๆ เช่น.map, .filter, .reduce, .sort, ,.every .someสำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการเหล่านี้ตรวจสอบวัตถุอาร์เรย์

ตัวอย่างที่ 1:for...ofวนรอบปกติ- ไม่มีลูกเล่นที่นี่

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

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

ตัวอย่างที่ 2:แบ่งคำเป็นอักขระ

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

ตัวอย่างที่ 3:วนรอบด้วยkeyและvalue

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

ตัวอย่างที่ 4: รับคุณสมบัติของวัตถุในบรรทัด

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

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

ตัวอย่างที่ 5: รับคุณสมบัติวัตถุลึกของสิ่งที่คุณต้องการ

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

ตัวอย่างที่ 6:เป็นตัวอย่าง 3 ที่ใช้กับ.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);
});

ตัวอย่างที่ 7:คือตัวอย่างที่ 4 ที่ใช้กับ.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)
});

ตัวอย่างที่ 8:คือตัวอย่าง 5 ที่ใช้กับ.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

วิธีที่ใกล้เคียงที่สุดกับความคิดของคุณคือการใช้Array.forEach()ซึ่งยอมรับฟังก์ชั่นการปิดซึ่งจะถูกดำเนินการสำหรับแต่ละองค์ประกอบของอาร์เรย์

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

อีกวิธีหนึ่งที่ใช้งานได้คือการใช้Array.map()ซึ่งทำงานในลักษณะเดียวกัน แต่ก็ต้องใช้ค่าทั้งหมดที่คุณส่งคืนและส่งกลับไปยังอาร์เรย์ใหม่ (โดยการจับคู่แต่ละองค์ประกอบกับองค์ประกอบใหม่) เช่นนี้:

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

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

1
สิ่งนี้ไม่ถูกต้องmapไม่ได้ทำการเปลี่ยนแปลงองค์ประกอบของอาเรย์เพราะมันจะส่งคืนอาเรย์ใหม่โดยที่ไอเท็มของอันใหม่นั้นเป็นผลมาจากการใช้ฟังก์ชั่นกับรายการของอาเรย์เก่า
Jesús Franco

ฉันไม่เคยบอกว่ามันไม่ส่งคืนอาร์เรย์ใหม่ฉันหมายถึงการเปลี่ยนแปลงที่ใช้โดยฟังก์ชัน แต่ที่นี่ฉันจะเปลี่ยนมันในคำตอบ
Ante Jablan Adamović

ผิดอีกครั้งแผนที่จะไม่แก้ไขหรือกลายพันธุ์รายการทั้งหมดในอาร์เรย์เดิมฉันได้เสนอและแก้ไขคำตอบที่เน้นแผนที่ทำงานกับสำเนาของรายการต้นฉบับโดยปล่อยให้อาร์เรย์เดิมไม่มีการแตะต้องเลย
Jesús Franco

7

คุณสามารถโทรหาแต่ละคนดังนี้:

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

Foreach นั้นเป็นฟังก์ชั่นการสั่งซื้อสูงซึ่งใช้ฟังก์ชั่นอื่นเป็นพารามิเตอร์

let theArray= [1,3,2];

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

เอาท์พุท:

1
3
2

นอกจากนี้คุณยังสามารถวนซ้ำแถวลำดับดังนี้:

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

6

หากคุณต้องการวนลูปผ่านอาร์เรย์ของวัตถุด้วยฟังก์ชั่นลูกศร:

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

ไวยากรณ์แลมบ์ดามักจะไม่ทำงานใน Internet Explorer 10 หรือต่ำกว่า

ฉันมักจะใช้

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

หากคุณเป็นแฟน jQuery และมีไฟล์ jQuery ทำงานอยู่คุณควรกลับตำแหน่งของดัชนีและพารามิเตอร์ค่า

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

5

หากคุณมีชุดมากมายคุณควรใช้iteratorsเพื่อเพิ่มประสิทธิภาพ Iterators เป็นทรัพย์สินของคอลเลกชัน JavaScript บางอย่าง (เช่นMap, Set, String, Array) แม้กระทั่งfor..ofใช้iteratorภายใต้ประทุน

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

คุณเข้าถึงรายการปัจจุบันโดยการเรียกใช้nextวิธีการตัววนซ้ำ วิธีถัดไปจะคืนค่าvalueรายการปัจจุบันและ a booleanเพื่อระบุว่าเมื่อใดที่คุณถึงจุดสิ้นสุดของการรวบรวม ต่อไปนี้เป็นตัวอย่างของการสร้างตัววนซ้ำจากอาร์เรย์

แปลงอาเรย์ปกติของคุณเป็นตัววนซ้ำโดยใช้values()วิธีดังนี้:

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

คุณสามารถเปลี่ยนอาเรย์ปกติของคุณเป็นตัววนซ้ำโดยใช้Symbol.iteratorดังนี้:

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

คุณยังสามารถเปลี่ยนรูปแบบปกติของคุณarrayเป็นiteratorแบบนี้ได้:

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}

หมายเหตุ :

  • Iterators มีความอ่อนไหวในธรรมชาติ
  • วัตถุไม่ได้เป็นiterableค่าเริ่มต้น ใช้for..inในกรณีนั้นเพราะแทนที่จะใช้ค่าจะทำงานกับคีย์

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับที่นี่ iteration protocol


5

หากคุณต้องการใช้forEach()มันจะมีลักษณะเหมือน -

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

หากคุณต้องการใช้for()มันจะมีลักษณะเหมือน -

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

4

ตามคุณลักษณะที่อัพเดตใหม่ ECMAScript 6 (ES6) และ ECMAScript 2015 คุณสามารถใช้ตัวเลือกต่อไปนี้พร้อมลูป:

สำหรับลูป

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

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

สำหรับ ... ในลูป

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

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

// Output: a,b

Array.forEach ()

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

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

// Output: 1,2,3,4

สำหรับ ... จากลูป

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

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

// Output: 1,2,3,4

ในขณะที่ลูป

let x = 0

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

// Output: 1,2,3,4

ทำ ... ในขณะที่ลูป

let x = 0

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

// Output: 1,2,3,4

4

ประสิทธิภาพ

วันนี้ (2019-12-18) ฉันทดสอบmacOS v10.13.6 (High Sierra) ของฉันบน Chrome v 79.0, Safari v13.0.4 และ Firefox v71.0 (64 บิต) - ข้อสรุปเกี่ยวกับการเพิ่มประสิทธิภาพ (และการเพิ่มประสิทธิภาพแบบไมโครซึ่ง มักจะไม่คุ้มค่าที่จะแนะนำให้รู้จักกับรหัสเพราะผลประโยชน์มีขนาดเล็ก แต่ความซับซ้อนของรหัสจะเพิ่มขึ้น)

  • ดูเหมือนว่าดั้งเดิมfor i( Aa ) เป็นตัวเลือกที่ดีในการเขียนโค้ดอย่างรวดเร็วบนเบราว์เซอร์ทั้งหมด

  • โซลูชันอื่น ๆ เช่นfor-of( โฆษณา ) ทั้งหมดในกลุ่มC. ... มักจะช้ากว่าAa 2 - 10 (และอื่น ๆ ) แต่สำหรับอาร์เรย์ขนาดเล็กก็สามารถใช้งานได้ - เพื่อประโยชน์ในการเพิ่มความชัดเจนของรหัส

  • ลูปที่มีความยาวของอาร์เรย์เก็บไว้ในn( Ab, Bb, Be ) บางครั้งก็เร็วกว่า แต่ก็ไม่ใช่ คอมไพเลอร์อาจตรวจจับสถานการณ์นี้โดยอัตโนมัติและแนะนำแคช ความแตกต่างระหว่างความเร็วเวอร์ชันแคชและไม่มีที่เก็บไว้ชั่วคราว ( Aa, บา, Bd ) ประมาณ ~ 1% เพื่อให้ดูเหมือนว่าแนะนำnเป็นไมโครเพิ่มประสิทธิภาพ

  • i--เช่นการแก้ปัญหาที่ห่วงเริ่มต้นจากองค์ประกอบอาร์เรย์ที่ผ่านมา ( Ac, BC ) มักจะ ~ 30% ช้ากว่าการแก้ปัญหาไปข้างหน้า - อาจเป็นเหตุผลทางของหน่วยความจำแคช CPU ทำงาน - อ่านหน่วยความจำไปข้างหน้าเป็นที่เหมาะสมมากขึ้นสำหรับการแคช CPU) ไม่แนะนำให้ใช้วิธีการแก้ปัญหาดังกล่าว

รายละเอียด

ในการทดสอบเราคำนวณผลรวมขององค์ประกอบอาร์เรย์ ฉันทำการทดสอบสำหรับอาร์เรย์ขนาดเล็ก (10 องค์ประกอบ) และอาร์เรย์ขนาดใหญ่ (องค์ประกอบ 1M) และแบ่งออกเป็นสามกลุ่ม:

  • เอ -forการทดสอบ
  • B -whileการทดสอบ
  • C - วิธีอื่น ๆ / ทางเลือก

ข้ามผลลัพธ์ของเบราว์เซอร์

ผลลัพธ์สำหรับเบราว์เซอร์ที่ทดสอบทั้งหมด

ป้อนคำอธิบายภาพที่นี่เบราว์เซอร์ **

อาเรย์กับ 10 องค์ประกอบ

ผลลัพธ์สำหรับ Chrome คุณสามารถดำเนินการทดสอบบนเครื่องของคุณที่นี่

ป้อนคำอธิบายภาพที่นี่

อาร์เรย์ที่มีองค์ประกอบ 1,000,000 รายการ

ผลลัพธ์สำหรับ Chrome คุณสามารถทำการทดสอบบนเครื่องของคุณได้ที่นี่

ป้อนคำอธิบายภาพที่นี่


4

สรุป:

เมื่อวนซ้ำแถวลำดับเรามักต้องการบรรลุหนึ่งในเป้าหมายต่อไปนี้:

  1. เราต้องการวนซ้ำอาร์เรย์และสร้างอาร์เรย์ใหม่:

    Array.prototype.map

  2. เราต้องการวนซ้ำอาร์เรย์และไม่สร้างอาร์เรย์ใหม่:

    Array.prototype.forEach

    for..of ห่วง

ใน JavaScript มีหลายวิธีในการบรรลุเป้าหมายทั้งสองนี้ อย่างไรก็ตามบางคนสะดวกกว่าคนอื่น ด้านล่างนี้คุณสามารถค้นหาวิธีที่ใช้กันทั่วไป (IMO ที่สะดวกที่สุด) เพื่อทำซ้ำอาร์เรย์ใน JavaScript

การสร้างอาร์เรย์ใหม่: Map

map()เป็นฟังก์ชั่นที่ตั้งอยู่Array.prototypeซึ่งสามารถแปลงทุกองค์ประกอบของอาเรย์แล้วส่งกลับอาร์เรย์ใหม่ map()ใช้เป็นอาร์กิวเมนต์สำหรับฟังก์ชันเรียกกลับและทำงานในลักษณะดังต่อไปนี้:

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

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

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

การเรียกกลับที่เราได้ส่งผ่านไปmap()เป็นอาร์กิวเมนต์จะถูกดำเนินการสำหรับทุกองค์ประกอบ จากนั้นอาร์เรย์จะได้รับคืนซึ่งมีความยาวเท่ากับอาร์เรย์เดิม ในองค์ประกอบอาร์เรย์ใหม่นี้จะถูกแปลงโดยฟังก์ชั่นการโทรกลับผ่านในฐานะที่เป็นอาร์กิวเมนต์map()ในองค์ประกอบอาร์เรย์ใหม่นี้จะถูกเปลี่ยนโดยฟังก์ชันการเรียกกลับผ่านเป็นอาร์กิวเมนต์ไปยัง

ความแตกต่างที่ชัดเจนระหว่างmapและกลไกลูปอื่นเช่นforEachและfor..ofลูปคือการmapส่งคืนอาเรย์ใหม่และปล่อยให้อาเรย์เก่ายังคงอยู่ (ยกเว้นถ้าคุณจัดการกับมันอย่างชัดเจนด้วยความคิดเช่นsplice )

นอกจากนี้โปรดทราบว่าการmapเรียกกลับของฟังก์ชันให้หมายเลขดัชนีของการวนซ้ำปัจจุบันเป็นอาร์กิวเมนต์ที่สอง นอกจากนี้อาร์กิวเมนต์ที่สามให้อาเรย์ที่mapถูกเรียก? บางครั้งคุณสมบัติเหล่านี้อาจมีประโยชน์มาก

วนรอบโดยใช้ forEach

forEachเป็นฟังก์ชั่นซึ่งตั้งอยู่Array.prototypeที่ใช้ฟังก์ชั่นการโทรกลับเป็นข้อโต้แย้ง จากนั้นจะเรียกใช้ฟังก์ชันการเรียกกลับนี้สำหรับทุกองค์ประกอบในอาร์เรย์ ตรงกันข้ามกับmap()ฟังก์ชั่นฟังก์ชั่น forEach จะไม่ส่งคืนอะไร ( undefined) ตัวอย่างเช่น:

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

เช่นเดียวกับmapฟังก์ชั่นการforEachโทรกลับให้หมายเลขดัชนีของการวนซ้ำปัจจุบันเป็นอาร์กิวเมนต์ที่สอง อาร์กิวเมนต์ที่สามจัดเตรียมอาร์เรย์ที่forEachเรียกว่าหรือไม่

วนรอบองค์ประกอบที่ใช้ for..of

การfor..ofวนซ้ำวนรอบองค์ประกอบของอาร์เรย์ทุกครั้ง (หรือวัตถุที่ทำซ้ำได้อื่น ๆ ) มันทำงานในลักษณะดังต่อไปนี้:

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

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

ในตัวอย่างข้างต้นelementหมายถึงองค์ประกอบอาร์เรย์และarrเป็นอาร์เรย์ที่เราต้องการวนรอบ สังเกตว่าชื่อelementนั้นเป็นโดยพลการและเราสามารถเลือกชื่ออื่น ๆ เช่น 'el' หรือชื่ออื่นที่มีความหมายมากกว่านี้ได้

อย่าสับสนระหว่างfor..inลูปกับfor..ofลูป for..inจะวนซ้ำผ่านคุณสมบัติที่นับได้ทั้งหมดของอาเรย์ในขณะที่for..ofลูปจะวนลูปผ่านองค์ประกอบอาเรย์เท่านั้น ตัวอย่างเช่น:

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

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