forEach ไม่ใช่ข้อผิดพลาดของฟังก์ชันกับอาร์เรย์ JavaScript


160

ฉันกำลังพยายามสร้างลูปง่ายๆ:

const parent = this.el.parentElement
console.log(parent.children)
parent.children.forEach(child => {
  console.log(child)
})

แต่ฉันได้รับข้อผิดพลาดต่อไปนี้:

VM384: 53 Uncaught TypeError: parent.children.forEach ไม่ใช่ฟังก์ชัน

แม้ว่าparent.childrenบันทึก:

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

อะไรคือปัญหา?

หมายเหตุ: นี่เป็นJSFiddle


ปัญหาเดียวกันเกิดขึ้นกับ element.siblings
Daut

@Daut ใช่เพราะ element.siblings ส่งคืน HTMLCollection และ HTMLCollections ไม่มีเมธอด forEach ()
Freddo

1
เฮ้คุณผู้ค้นหา Google! หากคุณกำลังอ่านสิ่งนี้ให้ตรวจสอบอีกครั้งว่าเป็นของแต่ละคนที่มีทุน E แทนที่จะเป็น foreach ....
Robert Sinclair

คำตอบ:


140

ตัวเลือกแรก: เรียกใช้ forEach โดยอ้อม

parent.childrenเป็นอาร์เรย์เช่นวัตถุ ใช้แนวทางต่อไปนี้:

const parent = this.el.parentElement;

Array.prototype.forEach.call(parent.children, child => {
  console.log(child)
});

parent.childrenมีNodeListชนิดซึ่งเป็นอาร์เรย์เช่นวัตถุเนื่องจาก:

  • ประกอบด้วยlengthคุณสมบัติซึ่งระบุจำนวนโหนด
  • แต่ละโหนดเป็นค่าคุณสมบัติที่มีชื่อตัวเลขโดยเริ่มจาก 0: {0: NodeObject, 1: NodeObject, length: 2, ...}

ดูรายละเอียดในบทความนี้


ตัวเลือกที่สอง: ใช้โปรโตคอลที่ทำซ้ำได้

parent.childrenคือHTMLCollection: ซึ่งใช้โปรโตคอลที่ทำซ้ำได้ ในสภาพแวดล้อม ES2015 คุณสามารถใช้HTMLCollectionกับโครงสร้างใด ๆ ที่ยอมรับการทำซ้ำ

ใช้HTMLCollectionกับตัวดำเนินการแพร่กระจาย:

const parent = this.el.parentElement;

[...parent.children].forEach(child => {
  console.log(child);
});

หรือด้วยfor..ofวงจร (ซึ่งเป็นตัวเลือกที่ฉันต้องการ):

const parent = this.el.parentElement;

for (const child of parent.children) {
  console.log(child);
}

เมื่อฉันใช้โซลูชันของคุณฉันไม่มีปัญหาอีกต่อไป แต่โค้ดภายในฟังก์ชันไม่ระบุตัวตนจะไม่ทำงาน .so ..
Jérémy

1
คุณใช้เบราว์เซอร์ใดเพื่อให้ parent.children บอกคุณว่าเป็น nodeList ใน Firefox มันบอกว่าเป็น HTMLCollection ถ้าเป็น nodeList .forEach () จะใช้งานได้
Freddo

117

parent.childrenไม่ใช่อาร์เรย์ เป็น HTMLCollection และไม่มีforEachวิธีการ คุณสามารถแปลงเป็นอาร์เรย์ก่อน ตัวอย่างเช่นใน ES6:

Array.from(parent.children).forEach(child => {
    console.log(child)
});

หรือใช้ตัวดำเนินการกระจาย:

[...parent.children].forEach(function (child) {
    console.log(child)
});

12
ฉันชอบโซลูชันนี้มากกว่าการยุ่งกับ Array ต้นแบบ
Daut

และคำตอบนี้คือ (หนึ่งใน) คำตอบที่ถูกต้องสำหรับคำถาม OP parent.children เป็น HTMLCollection ซึ่งไม่มี. forEach method
Freddo

19

parent.childrenจะกลับรายการโหนดรายการเทคนิคการเก็บ HTML นั่นคืออาร์เรย์เหมือนออบเจ็กต์ แต่ไม่ใช่อาร์เรย์ดังนั้นคุณจึงไม่สามารถเรียกใช้ฟังก์ชันอาร์เรย์ได้โดยตรง ในบริบทนี้คุณสามารถใช้Array.from()เพื่อแปลงเป็นอาร์เรย์จริง

Array.from(parent.children).forEach(child => {
  console.log(child)
})

ไม่ parent.children ไม่ส่งคืน nodeList แต่เป็นคอลเล็กชัน HTML ไม่ใช่สิ่งเดียวกัน. ถ้าเป็น nodeList, .forEach จะทำงาน
Freddo

15

เวอร์ชันที่ไร้เดียงสามากขึ้นอย่างน้อยคุณก็มั่นใจว่าจะใช้งานได้กับทุกอุปกรณ์โดยไม่มีการแปลงและ ES6:

const children = parent.children;
for (var i = 0; i < children.length; i++){
    console.log(children[i]);
}

https://jsfiddle.net/swb12kqn/5/


3
โหวตขึ้นเนื่องจากฟังก์ชั่น ES6 ใหม่ทั้งหมดเหล่านี้ทำสิ่งที่ดีเหมือนเดิมที่มีอยู่ แต่ในทางที่ยุ่งเหยิงเช่นเคยกับ JS
Freddo

1
ดีมาก .. ถ้าคุณไม่สามารถพิมพ์ผิดหรือกรอกรหัสได้
Tristanisginger

8

parent.childrenคือHTMLCollectionวัตถุที่มีลักษณะคล้ายอาร์เรย์ ขั้นแรกคุณต้องแปลงเป็นวิธีการArrayใช้งานArray.prototypeจริง

const parent = this.el.parentElement
console.log(parent.children)
[].slice.call(parent.children).forEach(child => {
  console.log(child)
})

2
หรือไม่แปลง แต่ใช้ use .call () on .forEach ()?
nnnnnn

@nnnnnn ดูคำตอบของฉันด้านล่าง
Dmitri Pavlutin

มีหลายวิธีในการแปลงวัตถุคล้ายอาร์เรย์เป็น Array :) นี่คือหนึ่งในนั้น
Dmitriy

@DmitriyLoskutov คุณไม่จำเป็นต้องแปลง - JavaScript เป็นภาษาสำหรับพิมพ์เป็ด เพียงแค่ใช้คุณสมบัตินี้
Dmitri Pavlutin

7

มีความจำเป็นในการที่ไม่มีforEachคุณสามารถย้ำใช้เพียงfrom's พารามิเตอร์ที่สองเช่นดังนั้น:

let nodeList = [{0: [{'a':1,'b':2},{'c':3}]},{1:[]}]
Array.from(nodeList, child => {
  console.log(child)
});


ข่าวเศร้าคือ parent.children ไม่ใช่ nodeList … .from () จะไม่ทำงาน
Freddo

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

ใช่ปัญหาคือคำถามของ OP เกี่ยวข้องกับคอลเล็กชัน HTML ไม่ใช่ nodeList ... ดังนั้นคำตอบก็ไม่ได้ตอบคำถาม
Freddo

@Cedric คำตอบนี้จะวนซ้ำในคอลเล็กชัน HTML เนื่องจากArray.fromจะแปลงวัตถุที่กำหนดในพารามิเตอร์แรกเป็นอาร์เรย์ ผลลัพธ์จะเหมือนกับในคำตอบของ madox2โดยไม่จำเป็นต้องforEachวนซ้ำ ( เอกสารArray.fromMDN )
Armfoot

5

นั่นเป็นเพราะparent.childrenเป็นNodeListและไม่รองรับ.forEachเมธอด (เนื่องจาก NodeList เป็นอาร์เรย์เหมือนโครงสร้าง แต่ไม่ใช่อาร์เรย์) ดังนั้นลองเรียกมันโดยการแปลงเป็นอาร์เรย์ก่อนโดยใช้

var children = [].slice.call(parent.children);
children.forEach(yourFunc);

ไม่ใช่ไม่ใช่ NodeList แต่เป็นคอลเล็กชัน HTML
Freddo

5

หากคุณกำลังพยายามวนซ้ำสิ่งNodeListนี้:

const allParagraphs = document.querySelectorAll("p");

ฉันขอแนะนำให้วนซ้ำด้วยวิธีนี้:

Array.prototype.forEach.call(allParagraphs , function(el) {
    // Write your code here
})

โดยส่วนตัวแล้วฉันลองมาหลายวิธีแล้ว แต่ส่วนใหญ่ไม่ได้ผลตามที่ฉันต้องการวนซ้ำNodeListแต่วิธีนี้ใช้งานได้ดีลองดูสิ!

NodeListไม่ได้เป็นอาร์เรย์ แต่เรารักษามันเป็นอาร์เรย์ใช้Array.ดังนั้นคุณจำเป็นต้องรู้ว่ามันไม่ได้รับการสนับสนุนในเบราว์เซอร์รุ่นเก่า!

ต้องการข้อมูลเพิ่มเติมเกี่ยวกับNodeList? โปรดอ่านของเอกสารเกี่ยวกับ MDN


1
คำตอบนี้ใช้ได้กับ nodeList อย่างชัดเจน ปัญหาคือ parent.children ส่งคืนคอลเล็กชัน HTML ซึ่งไม่ใช่ nodeList ...
Freddo

3

เนื่องจากคุณกำลังใช้คุณสมบัติของ ES6 ( ฟังก์ชั่นลูกศร ) คุณอาจใช้for loopดังนี้:

for(let child of [{0: [{'a':1,'b':2},{'c':3}]},{1:[]}]) {
  console.log(child)
}


โหวตแล้ว ช่างเป็นการบิดเบือนไวยากรณ์ ES6 แต่ ... ทำให้ฉันอยากร้องไห้และฉันมาจากพื้นหลัง C ++ ...
Freddo

1

คุณสามารถตรวจสอบว่าคุณพิมพ์forEachถูกต้องหรือไม่ถ้าคุณพิมพ์foreachเหมือนในภาษาโปรแกรมอื่น ๆ มันจะไม่ทำงาน


0

คุณสามารถใช้childNodesแทนchildren, childNodesนอกจากนี้ยังมีความน่าเชื่อถือมากขึ้นพิจารณาปัญหาความเข้ากันเบราว์เซอร์, ข้อมูลเพิ่มเติมที่นี่ :

parent.childNodes.forEach(function (child) {
    console.log(child)
});

หรือใช้ตัวดำเนินการกระจาย:

[...parent.children].forEach(function (child) {
    console.log(child)
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.