คู่มือฉบับสมบูรณ์ในการจัดการข้อผิดพลาดใน JavaScript

เผยแพร่แล้ว: 2022-01-24

กฎของเมอร์ฟีกล่าวว่าอะไรก็ตามที่สามารถผิดพลาดได้ก็จะผิดพลาดในที่สุด สิ่งนี้ใช้ได้ดีเกินไปในโลกของการเขียนโปรแกรม หากคุณสร้างแอปพลิเคชัน มีโอกาสที่คุณจะสร้างจุดบกพร่องและปัญหาอื่นๆ ข้อผิดพลาดใน JavaScript เป็นปัญหาทั่วไปอย่างหนึ่ง!

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

หากคุณกำลังสร้างแอปพลิเคชัน JavaScript มีโอกาสสูงที่คุณจะสับสนกับประเภทข้อมูล ณ จุดใดจุดหนึ่ง หากไม่เป็นเช่นนั้น คุณอาจจบลงด้วยการแทนที่ undefined ด้วย null หรือโอเปอเรเตอร์เท่ากับสามเท่า ( === ) ด้วยโอเปอเรเตอร์ double equals ( == )

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

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

เพื่อไม่ให้เป็นการเสียเวลา เรามาเริ่มกันเลย!

ข้อผิดพลาด JavaScript คืออะไร?

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

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

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

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

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

คุณสมบัติของข้อผิดพลาด

เมื่อคำจำกัดความของข้อผิดพลาด JavaScript ชัดเจนแล้ว ก็ถึงเวลาลงลึกในรายละเอียด

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

  1. message : ค่าสตริงที่มีข้อความแสดงข้อผิดพลาด
  2. name : ประเภทของข้อผิดพลาดที่เกิดขึ้น (เราจะเจาะลึกเรื่องนี้ในหัวข้อถัดไป)
  3. stack : การติดตามสแต็กของโค้ดที่เรียกใช้เมื่อเกิดข้อผิดพลาด

นอกจากนี้ ข้อผิดพลาดยังสามารถมีคุณสมบัติ เช่น columnNumber, lineNumber, fileName เป็นต้น เพื่ออธิบายข้อผิดพลาดได้ดีขึ้น อย่างไรก็ตาม คุณสมบัติเหล่านี้ไม่ได้มาตรฐานและอาจมีหรือไม่มีอยู่ในทุกอ็อบเจ็กต์ข้อผิดพลาดที่สร้างขึ้นจากแอปพลิเคชัน JavaScript ของคุณ

ทำความเข้าใจกับ Stack Trace

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

ข้อผิดพลาด "TypeError: คาดว่าอาร์กิวเมนต์ที่เป็นตัวเลข" จะแสดงบนพื้นหลังสีเทาพร้อมรายละเอียดสแต็กเพิ่มเติม
ตัวอย่างของ Stack Trace

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

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

ข้อผิดพลาดกับข้อยกเว้น

คนส่วนใหญ่มักจะถือว่าข้อผิดพลาดและข้อยกเว้นเป็นสิ่งเดียวกัน อย่างไรก็ตาม จำเป็นต้องสังเกตความแตกต่างเล็กน้อยแต่เป็นพื้นฐานระหว่างพวกเขา

เพื่อให้เข้าใจดีขึ้น เรามาดูตัวอย่างสั้นๆ กัน นี่คือวิธีที่คุณสามารถกำหนดข้อผิดพลาดใน JavaScript:

 const wrongTypeError = TypeError("Wrong type found, expected character")

และนี่คือวิธีที่ wrongTypeError ผิดประเภทข้อผิดพลาดกลายเป็นข้อยกเว้น:

 throw wrongTypeError

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

 throw TypeError("Wrong type found, expected character")

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

ประเภทของข้อผิดพลาดใน JavaScript

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

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

RangeError

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

สถานการณ์ทั่วไปบางประการที่ RangeError เกิดขึ้น ได้แก่:

  • พยายามสร้างอาร์เรย์ของความยาวที่ไม่ถูกต้องผ่านตัวสร้าง Array
  • การส่งค่าที่ไม่ถูกต้องไปยังวิธีการที่เป็นตัวเลข เช่น toExponential() , toPrecision() , toFixed() เป็นต้น
  • ส่งค่าที่ไม่ถูกต้องไปยังฟังก์ชันสตริงเช่น normalize()

อ้างอิงข้อผิดพลาด

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

สาเหตุทั่วไปบางประการที่ทำให้ ReferenceErrors เกิดขึ้น ได้แก่:

  • การพิมพ์ชื่อตัวแปรผิด
  • พยายามเข้าถึงตัวแปรที่กำหนดขอบเขตบล็อกนอกขอบเขต
  • การอ้างอิงตัวแปรส่วนกลางจากไลบรารีภายนอก (เช่น $ จาก jQuery) ก่อนที่จะโหลด

ข้อผิดพลาดทางไวยากรณ์

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

สาเหตุทั่วไปบางประการที่ SyntaxErrors อาจเกิดขึ้น ได้แก่:

  • ไม่มีเครื่องหมายจุลภาคกลับหัว
  • ไม่มีวงเล็บปิด
  • การจัดตำแหน่งวงเล็บปีกกาหรืออักขระอื่นๆ ไม่ถูกต้อง

เป็นการดีที่จะใช้เครื่องมือ linting ใน IDE ของคุณเพื่อระบุข้อผิดพลาดดังกล่าวให้กับคุณก่อนที่จะเข้าสู่เบราว์เซอร์

ประเภทข้อผิดพลาด

TypeError เป็นหนึ่งในข้อผิดพลาดที่พบบ่อยที่สุดในแอป JavaScript ข้อผิดพลาดนี้สร้างขึ้นเมื่อค่าบางอย่างไม่กลายเป็นประเภทที่คาดไว้ บางกรณีทั่วไปเมื่อมันเกิดขึ้นคือ:

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

มีความเป็นไปได้อีกมากมายที่ TypeError สามารถเกิดขึ้นได้ เราจะดูกรณีที่มีชื่อเสียงในภายหลังและเรียนรู้วิธีแก้ไข

InternalError

ชนิด InternalError จะใช้เมื่อมีข้อยกเว้นเกิดขึ้นในกลไกรันไทม์ของ JavaScript อาจหรืออาจไม่ระบุถึงปัญหาเกี่ยวกับรหัสของคุณ

บ่อยครั้ง InternalError เกิดขึ้นในสองสถานการณ์เท่านั้น:

  • เมื่อโปรแกรมแก้ไขหรืออัปเดตรันไทม์ JavaScript มีจุดบกพร่องที่ทำให้เกิดข้อยกเว้น (สิ่งนี้เกิดขึ้นน้อยมาก)
  • เมื่อโค้ดของคุณมีเอนทิตีที่มีขนาดใหญ่เกินไปสำหรับเอ็นจิ้น JavaScript (เช่น กรณีสวิตช์มากเกินไป ตัวเริ่มต้นอาร์เรย์ที่ใหญ่เกินไป การเรียกซ้ำมากเกินไป)

วิธีที่เหมาะสมที่สุดในการแก้ไขข้อผิดพลาดนี้คือการระบุสาเหตุผ่านข้อความแสดงข้อผิดพลาดและปรับโครงสร้างตรรกะของแอป หากเป็นไปได้ เพื่อขจัดภาระงานที่เพิ่มขึ้นอย่างกะทันหันในกลไก JavaScript

URIError

URIError เกิดขึ้นเมื่อใช้ฟังก์ชันจัดการ URI ส่วนกลาง เช่น decodeURIComponent อย่างผิดกฎหมาย โดยปกติบ่งชี้ว่าพารามิเตอร์ที่ส่งผ่านไปยังการเรียกเมธอดไม่สอดคล้องกับมาตรฐาน URI ดังนั้นจึงไม่มีการแยกวิเคราะห์โดยเมธอดอย่างถูกต้อง

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

EvalError

EvalError เกิดขึ้นเมื่อเกิดข้อผิดพลาดกับการเรียกใช้ฟังก์ชัน eval() eval() ใช้เพื่อรันโค้ด JavaScript ที่เก็บไว้ในสตริง อย่างไรก็ตาม เนื่องจากเราไม่แนะนำให้ใช้ eval() เนื่องจากปัญหาด้านความปลอดภัยและข้อกำหนดของ ECMAScript ปัจจุบันไม่ส่งคลาส EvalError อีกต่อไป ข้อผิดพลาดประเภทนี้จึงมีอยู่เพียงเพื่อรักษาความเข้ากันได้แบบย้อนหลังกับโค้ด JavaScript รุ่นเก่า

หากคุณกำลังใช้งาน JavaScript เวอร์ชันเก่า คุณอาจพบข้อผิดพลาดนี้ ไม่ว่าในกรณีใด จะเป็นการดีที่สุดที่จะตรวจสอบโค้ดที่รันในการเรียกใช้ฟังก์ชัน eval() เพื่อหาข้อยกเว้น

การสร้างประเภทข้อผิดพลาดที่กำหนดเอง

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

ดังนั้น ในทางเทคนิค ข้อความเหล่านี้จึงถูกกฎหมายทั้งหมด:

 throw 8 throw "An error occurred"

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

คุณสามารถขยายคลาส Error เพื่อสร้างคลาสข้อผิดพลาดที่กำหนดเองได้ ต่อไปนี้คือตัวอย่างพื้นฐานเกี่ยวกับวิธีการทำเช่นนี้:

 class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; } }

และคุณสามารถใช้ในลักษณะต่อไปนี้:

 throw ValidationError("Property not found: name")

จากนั้นคุณสามารถระบุได้โดยใช้ instanceof ของคำหลัก:

 try { validateForm() // code that throws a ValidationError } catch (e) { if (e instanceof ValidationError) // do something else // do something else }

10 อันดับข้อผิดพลาดที่พบบ่อยที่สุดใน JavaScript

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

1. Uncaught RangeError

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

ข้อผิดพลาด "Uncaught RangeError: เกินขนาดสแต็กการโทรสูงสุด" จะแสดงบนพื้นหลังสีแดงข้างไอคอนกากบาทสีแดงที่มีโค้ดของฟังก์ชันแบบเรียกซ้ำอยู่ด้านบน
ตัวอย่าง RangeError กับการเรียกใช้ฟังก์ชันแบบเรียกซ้ำ

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

ข้อผิดพลาด “Uncaught RangeError: toExponential() อาร์กิวเมนต์ต้องอยู่ระหว่าง 0 ถึง 100” จะแสดงบนพื้นหลังสีแดงข้างไอคอนกากบาทสีแดงที่มีการเรียกใช้ฟังก์ชัน toExponential() ด้านบน
ตัวอย่าง RangeError กับการเรียก toExponential()

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

num = 4. num.toExponential(2). เอาท์พุต: 4.00e+0
เอาต์พุตสำหรับการเรียกใช้ฟังก์ชัน toExponential()

2. Uncaught TypeError: ไม่สามารถตั้งค่าคุณสมบัติ

ข้อผิดพลาดนี้เกิดขึ้นเมื่อคุณตั้งค่าคุณสมบัติในการอ้างอิงที่ไม่ได้กำหนด คุณสามารถทำให้เกิดปัญหาซ้ำได้ด้วยรหัสนี้:

 var list list.count = 0

นี่คือผลลัพธ์ที่คุณจะได้รับ:

ข้อผิดพลาด Uncaught TypeError: ไม่สามารถตั้งค่าคุณสมบัติของไม่ได้กำหนด จะแสดงบนพื้นหลังสีแดงข้างไอคอนกากบาทสีแดงที่มีรายการ list.count = 0 ด้านบน
ตัวอย่าง TypeError

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

การตั้งค่า list.count = 10 หลังจากเริ่มต้นรายการด้วย {} เนื่องจากผลลัพธ์คือ 10
วิธีแก้ไข TypeError

3. Uncaught TypeError: ไม่สามารถอ่านคุณสมบัติ

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

 var func func.call()

นี่คือผลลัพธ์:

ข้อผิดพลาด Uncaught TypeError: ไม่สามารถอ่านคุณสมบัติของ undefined ปรากฏบนพื้นหลังสีแดงข้างไอคอนกากบาทสีแดงที่มี func.call() อยู่ด้านบน
ตัวอย่าง TypeError พร้อมฟังก์ชันที่ไม่ได้กำหนด

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

 import React, { useState, useEffect } from "react"; const CardsList = () => { const [state, setState] = useState(); useEffect(() => { setTimeout(() => setState({ items: ["Card 1", "Card 2"] }), 2000); }, []); return ( <> {state.items.map((item) => ( <li key={item}>{item}</li> ))} </> ); }; export default CardsList;

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

ข้อผิดพลาด "undefined is not an object" แสดงบนพื้นหลังสีเทา
การติดตามสแต็ก TypeError ในเบราว์เซอร์

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

 // ... const [state, setState] = useState({items: []}); // ...

ตอนนี้ หลังจากหน่วงเวลาที่ตั้งไว้ แอปของคุณจะแสดงผลลัพธ์ที่คล้ายคลึงกัน:

รายการหัวข้อย่อยที่มีสองรายการที่อ่านว่า "การ์ด 1" และ "การ์ด 2"
เอาต์พุตรหัส

การแก้ไขที่แน่นอนในโค้ดของคุณอาจแตกต่างกัน แต่สิ่งสำคัญที่นี่คือการเริ่มต้นตัวแปรของคุณอย่างถูกต้องเสมอก่อนที่จะใช้งาน

4. TypeError: 'undefined' ไม่ใช่วัตถุ

ข้อผิดพลาดนี้เกิดขึ้นใน Safari เมื่อคุณพยายามเข้าถึงคุณสมบัติของหรือเรียกใช้เมธอดบนวัตถุที่ไม่ได้กำหนด คุณสามารถเรียกใช้โค้ดเดียวกันจากด้านบนเพื่อสร้างข้อผิดพลาดซ้ำได้ด้วยตนเอง

ข้อผิดพลาด “TypeError: undefined is not an object” แสดงบนพื้นหลังสีแดงข้างไอคอนเครื่องหมายอัศเจรีย์สีแดงที่มีการเรียกใช้เมธอด func.call() ด้านบน
ตัวอย่าง TypeError พร้อมฟังก์ชันที่ไม่ได้กำหนด

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

5. TypeError: null ไม่ใช่วัตถุ

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

 var func = null func.call()

นี่คือผลลัพธ์ที่คุณจะได้รับ:

ข้อความแสดงข้อผิดพลาด "TypeError: null is not an object" แสดงบนพื้นหลังสีแดงข้างไอคอนเครื่องหมายอัศเจรีย์สีแดง
ตัวอย่าง TypeError ที่มีฟังก์ชัน null

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

6. TypeError: ไม่สามารถอ่านคุณสมบัติ 'ความยาว'

ข้อผิดพลาดนี้เกิดขึ้นใน Chrome เมื่อคุณพยายามอ่านความยาวของวัตถุที่เป็น null หรือ undefined สาเหตุของปัญหานี้คล้ายกับปัญหาก่อนหน้านี้ แต่เกิดขึ้นค่อนข้างบ่อยขณะจัดการรายการ จึงสมควรได้รับการกล่าวถึงเป็นพิเศษ ต่อไปนี้คือวิธีที่คุณสามารถทำให้เกิดปัญหาซ้ำได้:

ข้อผิดพลาด Uncaught TypeError: ไม่สามารถอ่านคุณสมบัติ 'ความยาว' ของไม่ได้กำหนด” ที่แสดงบนพื้นหลังสีแดงข้างไอคอนกากบาทสีแดงที่มีการเรียก myButton.length ด้านบน
ตัวอย่าง TypeError กับวัตถุที่ไม่ได้กำหนด

อย่างไรก็ตาม ใน Chrome เวอร์ชันใหม่กว่า ข้อผิดพลาดนี้ถูกรายงานเป็น Uncaught TypeError: Cannot read properties of undefined นี่คือลักษณะที่ปรากฏในขณะนี้:

ข้อผิดพลาด Uncaught TypeError: ไม่สามารถอ่านคุณสมบัติของ undefined ปรากฏบนพื้นหลังสีแดงข้างไอคอนกากบาทสีแดงที่มีการเรียก myButton.length ด้านบน
ตัวอย่าง TypeError กับวัตถุที่ไม่ได้กำหนดใน Chrome เวอร์ชันใหม่กว่า

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

7. TypeError: 'undefined' ไม่ใช่ฟังก์ชัน

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

หากคุณใช้คีย์เวิร์ดที่อ้างอิงตัวเอง this ในโค้ดของคุณ ข้อผิดพลาด this อาจเกิดขึ้นหากไม่เชื่อมโยงกับบริบทของคุณอย่างเหมาะสม พิจารณารหัสต่อไปนี้:

 function showAlert() { alert("message here") } document.addEventListener("click", () => { this.showAlert(); })

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

ในทางตรงกันข้าม ฟังก์ชัน showAlert ถูกกำหนดไว้ในบริบทของ window

ในการแก้ปัญหานี้ คุณต้องส่งการอ้างอิงที่เหมาะสมไปยังฟังก์ชันโดยผูกมันด้วยเมธอด bind() :

 document.addEventListener("click", this.showAlert.bind(this))

8. ReferenceError: เหตุการณ์ไม่ได้ถูกกำหนด

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

ข้อผิดพลาดนี้อาจไม่เกิดขึ้นใน Internet Explorer หรือ Google Chrome (เนื่องจาก IE มีตัวแปรเหตุการณ์ส่วนกลางและ Chrome จะแนบตัวแปรเหตุการณ์เข้ากับตัวจัดการโดยอัตโนมัติ) แต่อาจเกิดขึ้นได้ใน Firefox ดังนั้นจึงแนะนำให้จับตาดูข้อผิดพลาดเล็กๆ น้อยๆ ดังกล่าว

9. TypeError: การมอบหมายให้ตัวแปรคงที่

เป็นความผิดที่เกิดจากความไม่ประมาท หากคุณพยายามกำหนดค่าใหม่ให้กับตัวแปรคงที่ คุณจะได้พบกับผลลัพธ์ดังกล่าว:

ข้อผิดพลาด Uncaught TypeError: การมอบหมายให้ตัวแปรคงที่ แสดงบนพื้นหลังสีแดงข้างไอคอนกากบาทสีแดงที่มีการกำหนด func = 6 ด้านบน
ตัวอย่าง TypeError กับการกำหนดวัตถุคงที่

แม้ว่าตอนนี้จะดูเหมือนง่ายที่จะแก้ไข ลองนึกภาพการประกาศตัวแปรหลายร้อยรายการ และหนึ่งในนั้นถูกกำหนดอย่างผิดพลาดเป็น const แทน let ! ไม่เหมือนกับภาษาสคริปต์อื่นๆ เช่น PHP รูปแบบของการประกาศค่าคงที่และตัวแปรใน JavaScript มีความแตกต่างกันเพียงเล็กน้อย ดังนั้นจึงแนะนำให้ตรวจสอบการประกาศของคุณก่อนเมื่อคุณพบข้อผิดพลาดนี้ คุณอาจพบข้อผิดพลาดนี้หากคุณ ลืม ว่าการอ้างอิงดังกล่าวเป็นค่าคงที่และใช้เป็นตัวแปร สิ่งนี้บ่งบอกถึงความประมาทหรือข้อบกพร่องในตรรกะของแอปของคุณ ตรวจสอบให้แน่ใจว่าได้ตรวจสอบสิ่งนี้เมื่อพยายามแก้ไขปัญหานี้

10. (ไม่ทราบ): ข้อผิดพลาดของสคริปต์

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

คุณไม่สามารถแก้ไขข้อผิดพลาดนี้โดยไม่ทราบรายละเอียดทั้งหมด สิ่งที่คุณทำได้เพื่อรับข้อมูลเพิ่มเติมเกี่ยวกับข้อผิดพลาดมีดังนี้

  1. เพิ่มแอตทริบิวต์ crossorigin ในแท็กสคริปต์
  2. ตั้งค่าส่วนหัว Access-Control-Allow-Origin ที่ถูกต้องบนเซิร์ฟเวอร์ที่โฮสต์สคริปต์
  3. [ไม่บังคับ] หากคุณไม่มีสิทธิ์เข้าถึงเซิร์ฟเวอร์ที่โฮสต์สคริปต์ คุณสามารถลองใช้พร็อกซีเพื่อส่งต่อคำขอของคุณไปยังเซิร์ฟเวอร์และกลับไปที่ไคลเอ็นต์ด้วยส่วนหัวที่ถูกต้อง

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

วิธีระบุและป้องกันข้อผิดพลาดใน JavaScript

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

โยนและจับข้อผิดพลาดด้วยตนเอง

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

โยน

คีย์เวิร์ดแรกและพื้นฐานที่สุดของชุดคือ throw ดังที่เห็นได้ชัด คีย์เวิร์ด throw ถูกใช้เพื่อสร้างข้อผิดพลาดเพื่อสร้างข้อยกเว้นในรันไทม์ JavaScript ด้วยตนเอง เราได้พูดถึงเรื่องนี้ไปก่อนหน้านี้แล้ว และนี่คือส่วนสำคัญของความสำคัญของคีย์เวิร์ดนี้:

  • คุณสามารถ throw อะไรก็ได้ รวมถึงตัวเลข สตริง และอ็อบเจ็กต์ Error
  • อย่างไรก็ตาม ไม่แนะนำให้โยนประเภทข้อมูลพื้นฐาน เช่น สตริงและตัวเลข เนื่องจากไม่มีข้อมูลการดีบักเกี่ยวกับข้อผิดพลาด
  • ตัวอย่าง: throw TypeError("Please provide a string")

พยายาม

คีย์เวิร์ด try ใช้เพื่อระบุว่าบล็อกของโค้ดอาจมีข้อยกเว้น ไวยากรณ์ของมันคือ:

 try { // error-prone code here }

สิ่งสำคัญที่ควรทราบคือบล็อก catch จะต้องติดตาม try block เสมอเพื่อจัดการกับข้อผิดพลาดอย่างมีประสิทธิภาพ

จับ

คีย์เวิร์ด catch ใช้เพื่อสร้างบล็อก catch บล็อกของรหัสนี้รับผิดชอบในการจัดการข้อผิดพลาดที่บล็อกการ try ต่อท้ายจับได้ นี่คือไวยากรณ์:

 catch (exception) { // code to handle the exception here }

และนี่คือวิธีที่คุณใช้บล็อก try และ catch ร่วมกัน:

 try { // business logic code } catch (exception) { // error handling code }

ไม่เหมือนกับ C++ หรือ Java คุณไม่สามารถผนวกบล็อก catch หลายบล็อกเข้ากับบล็อก try ใน JavaScript ซึ่งหมายความว่าคุณไม่สามารถทำได้:

 try { // business logic code } catch (exception) { if (exception instanceof TypeError) { // do something } } catch (exception) { if (exception instanceof RangeError) { // do something } }

คุณสามารถใช้คำสั่ง if...else หรือคำสั่ง switch case ภายในบล็อก catch เดียวเพื่อจัดการกับกรณีข้อผิดพลาดที่เป็นไปได้ทั้งหมด มันจะมีลักษณะเช่นนี้:

 try { // business logic code } catch (exception) { if (exception instanceof TypeError) { // do something } else if (exception instanceof RangeError) { // do something else } }

ในที่สุด

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

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

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

จัดการข้อผิดพลาดทั่วโลกด้วย onerror() Method

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

โดยปกติ คุณจะต้องระบุ URL รูปภาพอื่นในการเรียก onerror เพื่อให้แท็ก img ถอยกลับไป นี่คือวิธีที่คุณสามารถทำได้ผ่าน JavaScript:

 const image = document.querySelector("img") image.onerror = (event) => { console.log("Error occurred: " + event) }

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

 window.onerror = (event) => { console.log("Error occurred: " + event) }

ด้วยตัวจัดการเหตุการณ์นี้ คุณสามารถกำจัดบล็อก try...catch จำนวนมากที่อยู่ในโค้ดของคุณ และรวมศูนย์การจัดการข้อผิดพลาดของแอปได้เหมือนกับการจัดการเหตุการณ์ คุณสามารถแนบตัวจัดการข้อผิดพลาดหลายตัวเข้ากับหน้าต่างเพื่อรักษา Single Responsibility Principle จากหลักการออกแบบ SOLID ล่ามจะวนไปตามตัวจัดการทั้งหมดจนกว่าจะถึงตัวที่เหมาะสม

ส่งข้อผิดพลาดผ่านการโทรกลับ

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

พิจารณาชิ้นส่วนของรหัสต่อไปนี้:

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

 const calculateCube = (number, callback) => { setTimeout(() => { const cube = number * number * number callback(cube) }, 1000) } const callback = result => console.log(result) calculateCube(4, callback)

ฟังก์ชันข้างต้นแสดงสภาวะแบบอะซิงโครนัสซึ่งฟังก์ชันใช้เวลาในการประมวลผลการดำเนินการและส่งคืนผลลัพธ์ในภายหลังด้วยความช่วยเหลือของการเรียกกลับ

หากคุณพยายามป้อนสตริงแทนที่จะเป็น 4 ในการเรียกใช้ฟังก์ชัน คุณจะได้ผลลัพธ์ NaN

สิ่งนี้จะต้องได้รับการจัดการอย่างถูกต้อง โดยใช้วิธีดังนี้:

 const calculateCube = (number, callback) => { setTimeout(() => { if (typeof number !== "number") throw new Error("Numeric argument is expected") const cube = number * number * number callback(cube) }, 1000) } const callback = result => console.log(result) try { calculateCube(4, callback) } catch (e) { console.log(e) }

สิ่งนี้ควรแก้ปัญหาได้อย่างดีเยี่ยม อย่างไรก็ตาม หากคุณลองส่งสตริงไปยังการเรียกใช้ฟังก์ชัน คุณจะได้รับสิ่งนี้:

ข้อผิดพลาด "Uncaught Error: คาดว่าอาร์กิวเมนต์ที่เป็นตัวเลข" แสดงบนพื้นหลังสีแดงเข้มข้างไอคอนกากบาทสีแดง
ตัวอย่างข้อผิดพลาดที่มีอาร์กิวเมนต์ที่ไม่ถูกต้อง

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

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

นี่คือวิธีที่คุณสามารถจัดการกับข้อผิดพลาดได้อย่างถูกต้องในการเรียกกลับ:

 const calculateCube = (number, callback) => { setTimeout(() => { if (typeof number !== "number") { callback(new TypeError("Numeric argument is expected")) return } const cube = number * number * number callback(null, cube) }, 2000) } const callback = (error, result) => { if (error !== null) { console.log(error) return } console.log(result) } try { calculateCube('hey', callback) } catch (e) { console.log(e) }

ตอนนี้ผลลัพธ์ที่คอนโซลจะเป็น:

ข้อความ "TypeError: คาดว่าอาร์กิวเมนต์ที่เป็นตัวเลข" แสดงบนพื้นหลังสีเทาเข้ม
ตัวอย่าง TypeError พร้อมอาร์กิวเมนต์ที่ผิดกฎหมาย

ซึ่งบ่งชี้ว่าข้อผิดพลาดได้รับการจัดการอย่างเหมาะสม

จัดการข้อผิดพลาดในสัญญา

คนส่วนใหญ่มักจะชอบสัญญาในการจัดการกิจกรรมแบบอะซิงโครนัส สัญญามีข้อได้เปรียบอีกประการหนึ่ง — คำสัญญาที่ถูกปฏิเสธจะไม่ทำให้สคริปต์ของคุณสิ้นสุดลง อย่างไรก็ตาม คุณยังคงต้องใช้ catch block เพื่อจัดการกับข้อผิดพลาดในสัญญา เพื่อให้เข้าใจได้ดีขึ้น เรามาเขียนฟังก์ชัน calcCube calculateCube() ใหม่โดยใช้ Promises กัน:

 const delay = ms => new Promise(res => setTimeout(res, ms)); const calculateCube = async (number) => { if (typeof number !== "number") throw Error("Numeric argument is expected") await delay(5000) const cube = number * number * number return cube } try { calculateCube(4).then(r => console.log(r)) } catch (e) { console.log(e) }

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

ข้อผิดพลาด "Uncaught (ตามสัญญา) ข้อผิดพลาด: คาดว่าอาร์กิวเมนต์ที่เป็นตัวเลข" แสดงบนพื้นหลังสีเทาเข้มข้างไอคอนกากบาทสีแดง
ตัวอย่าง TypeError พร้อมอาร์กิวเมนต์ที่ผิดกฎหมายใน Promise

นี่เป็นเพราะ Promise โยนข้อผิดพลาดหลังจากที่ทุกอย่างเสร็จสิ้นการดำเนินการ วิธีแก้ปัญหานี้ง่าย เพียงเพิ่มการเรียก catch() ไปยังกลุ่มสัญญาดังนี้:

 calculateCube("hey") .then(r => console.log(r)) .catch(e => console.log(e))

ตอนนี้ผลลัพธ์จะเป็น:

ข้อความ "ข้อผิดพลาด: คาดว่าอาร์กิวเมนต์ที่เป็นตัวเลข" แสดงบนพื้นหลังสีเทาเข้ม
ตัวอย่างการจัดการ TypeError พร้อมอาร์กิวเมนต์ที่ผิดกฎหมาย

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

อีกทางหนึ่ง คุณยังสามารถจัดการกับข้อผิดพลาดในสัญญาโดยใช้เทคนิค try-catch-finly แบบเดิม ในกรณีนี้การเรียกตามคำสัญญาของคุณจะมีลักษณะดังนี้:

 try { let result = await calculateCube("hey") console.log(result) } catch (e) { console.log(e) } finally { console.log('Finally executed") }

อย่างไรก็ตาม การทำงานนี้ทำได้ภายในฟังก์ชันแบบอะซิงโครนัสเท่านั้น ดังนั้น วิธีที่นิยมใช้มากที่สุดในการจัดการข้อผิดพลาดในสัญญาคือการ catch ลูกโซ่และ finally เป็นการเรียกตามสัญญา

throw/catch vs onerror() vs Callbacks vs Promises: อันไหนดีที่สุด?

ด้วยสี่วิธีที่คุณต้องการ คุณต้องรู้วิธีเลือกวิธีที่เหมาะสมที่สุดในกรณีการใช้งานที่กำหนด วิธีตัดสินใจด้วยตัวเองมีดังนี้

โยน/จับ

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

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

เมื่อตัดสินใจเลือกระหว่างบล็อก try/catch แบบอะซิงโครนัสและ catch() ของสัญญา ขอแนะนำให้ใช้บล็อก try/catch แบบอะซิงโครนัส เนื่องจากพวกมันจะทำให้โค้ดของคุณเป็นแบบเส้นตรงและง่ายต่อการดีบั๊ก

ข้อผิดพลาด ()

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

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

โทรกลับและสัญญา

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

นี่เป็นเพราะคำสัญญามีโครงสร้างที่ฝังไว้สำหรับการโยง catch() และบล็อก finally() เพื่อจัดการกับข้อผิดพลาดได้อย่างง่ายดาย This method is easier and cleaner than defining additional arguments/reusing existing arguments to handle errors.

Keep Track of Changes With Git Repositories

Many errors often arise due to manual mistakes in the codebase. While developing or debugging your code, you might end up making unnecessary changes that may cause new errors to appear in your codebase. Automated testing is a great way to keep your code in check after every change. However, it can only tell you if something's wrong. If you don't take frequent backups of your code, you'll end up wasting time trying to fix a function or a script that was working just fine before.

This is where git plays its role. With a proper commit strategy, you can use your git history as a backup system to view your code as it evolved through the development. You can easily browse through your older commits and find out the version of the function working fine before but throwing errors after an unrelated change.

You can then restore the old code or compare the two versions to determine what went wrong. Modern web development tools like GitHub Desktop or GitKraken help you to visualize these changes side by side and figure out the mistakes quickly.

A habit that can help you make fewer errors is running code reviews whenever you make a significant change to your code. If you're working in a team, you can create a pull request and have a team member review it thoroughly. This will help you use a second pair of eyes to spot out any errors that might have slipped by you.

Best Practices for Handling Errors in JavaScript

The above-mentioned methods are adequate to help you design a robust error handling approach for your next JavaScript application. However, it would be best to keep a few things in mind while implementing them to get the best out of your error-proofing. Here are some tips to help you.

1. Use Custom Errors When Handling Operational Exceptions

We introduced custom errors early in this guide to give you an idea of how to customize the error handling to your application's unique case. It's advisable to use custom errors wherever possible instead of the generic Error class as it provides more contextual information to the calling environment about the error.

On top of that, custom errors allow you to moderate how an error is displayed to the calling environment. This means that you can choose to hide specific details or display additional information about the error as and when you wish.

You can go so far as to format the error contents according to your needs. This gives you better control over how the error is interpreted and handled.

2. Do Not Swallow Any Exceptions

Even the most senior developers often make a rookie mistake — consuming exceptions levels deep down in their code.

You might come across situations where you have a piece of code that is optional to run. If it works, great; if it doesn't, you don't need to do anything about it.

In these cases, it's often tempting to put this code in a try block and attach an empty catch block to it. However, by doing this, you'll leave that piece of code open to causing any kind of error and getting away with it. This can become dangerous if you have a large codebase and many instances of such poor error management constructs.

The best way to handle exceptions is to determine a level on which all of them will be dealt and raise them until there. This level can be a controller (in an MVC architecture app) or a middleware (in a traditional server-oriented app).

This way, you'll get to know where you can find all the errors occurring in your app and choose how to resolve them, even if it means not doing anything about them.

3. Use a Centralized Strategy for Logs and Error Alerts

Logging an error is often an integral part of handling it. Those who fail to develop a centralized strategy for logging errors may miss out on valuable information about their app's usage.

An app's event logs can help you figure out crucial data about errors and help to debug them quickly. If you have proper alerting mechanisms set up in your app, you can know when an error occurs in your app before it reaches a large section of your user base.

It's advisable to use a pre-built logger or create one to suit your needs. You can configure this logger to handle errors based on their levels (warning, debug, info, etc.), and some loggers even go so far as to send logs to remote logging servers immediately. This way, you can watch how your application's logic performs with active users.

4. Notify Users About Errors Appropriately

Another good point to keep in mind while defining your error handling strategy is to keep the user in mind.

All errors that interfere with the normal functioning of your app must present a visible alert to the user to notify them that something went wrong so the user can try to work out a solution. If you know a quick fix for the error, such as retrying an operation or logging out and logging back in, make sure to mention it in the alert to help fix the user experience in real-time.

In the case of errors that don't cause any interference with the everyday user experience, you can consider suppressing the alert and logging the error to a remote server for resolving later.

5. Implement a Middleware (Node.js)

The Node.js environment supports middlewares to add functionalities to server applications. You can use this feature to create an error-handling middleware for your server.

The most significant benefit of using middleware is that all of your errors are handled centrally in one place. You can choose to enable/disable this setup for testing purposes easily.

Here's how you can create a basic middleware:

const logError = err => { console.log("ERROR: " + String(err)) } const errorLoggerMiddleware = (err, req, res, next) => { logError(err) next(err) } const returnErrorMiddleware = (err, req, res, next) => { res.status(err.statusCode || 500) .send(err.message) } module.exports = { logError, errorLoggerMiddleware, returnErrorMiddleware }

จากนั้นคุณสามารถใช้มิดเดิลแวร์นี้ในแอปของคุณดังนี้:

 const { errorLoggerMiddleware, returnErrorMiddleware } = require('./errorMiddleware') app.use(errorLoggerMiddleware) app.use(returnErrorMiddleware)

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

6. รีสตาร์ทแอปของคุณเพื่อจัดการกับข้อผิดพลาดของโปรแกรมเมอร์ (Node.js)

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

7. จับข้อยกเว้นที่ไม่ถูกตรวจจับทั้งหมด (Node.js)

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

นี่คือวิธีที่คุณสามารถทำได้:

 process.on('uncaughtException', error => { console.log("ERROR: " + String(error)) // other handling mechanisms })

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

8. จับการปฏิเสธสัญญาที่ไม่มีการจัดการทั้งหมด (Node.js)

เช่นเดียวกับที่คุณไม่สามารถครอบคลุมข้อยกเว้นที่เป็นไปได้ทั้งหมด มีโอกาสสูงที่คุณอาจพลาดการจัดการกับการปฏิเสธสัญญาที่เป็นไปได้ทั้งหมด อย่างไรก็ตาม สัญญาการปฏิเสธจะไม่เกิดข้อผิดพลาดต่างจากข้อยกเว้น

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

นี่คือวิธีที่คุณสามารถทำได้:

 const promiseRejectionCallback = error => { console.log("PROMISE REJECTED: " + String(error)) } process.on('unhandledRejection', callback)
หากคุณสร้างแอปพลิเคชัน มีโอกาสที่คุณจะสร้างจุดบกพร่องและปัญหาอื่นๆ ด้วย เรียนรู้วิธีจัดการกับมันด้วยความช่วยเหลือจากคู่มือนี้ ️ คลิกเพื่อทวีต

สรุป

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

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

ในกรณีส่วนใหญ่ กลยุทธ์ที่มั่นคงในการจัดการข้อผิดพลาดด้วยการดำเนินการอย่างระมัดระวังก็เพียงพอแล้วสำหรับแอปพลิเคชัน JavaScript ทุกประเภท

มีข้อผิดพลาด JavaScript อื่นๆ ที่คุณยังไม่สามารถแก้ไขได้หรือไม่ เทคนิคใดในการจัดการข้อผิดพลาด JS อย่างสร้างสรรค์? แจ้งให้เราทราบในความคิดเห็นด้านล่าง!