5 คะแนน โดย GN⁺ 2023-11-12 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • หากคุณใช้งานเครื่องมือสำหรับนักพัฒนาในเบราว์เซอร์ได้ในระดับกลางขึ้นไป คุณสามารถขยาย conditional breakpoint ให้เป็นมากกว่าเงื่อนไขหยุดธรรมดา และใช้เป็นเครื่องมือสำหรับบันทึกล็อก ติดตามสถานะ เปลี่ยนสถานะ และวัดเวลาได้
  • ถ้านำ Console API อย่าง console.log, console.count, console.trace, console.time ไปใส่ไว้ใน breakpoint หรือ Watch Pane ก็จะสามารถบันทึก flow การทำงานและสถานะได้โดยไม่ต้องหยุดการทำงาน
  • เมื่อผสานจำนวนอาร์กิวเมนต์ของฟังก์ชัน เวลา ค่า CSS ที่คำนวณแล้ว จำนวนครั้งที่รัน การสุ่มตัวอย่าง และ global boolean เข้าด้วยกัน ก็จะควบคุม จังหวะที่หยุด ได้ละเอียดขึ้นมาก
  • monitor, debug, copy, $0, getEventListeners, monitorEvents ของ Chrome ช่วยให้จัดการการเรียกใช้ฟังก์ชัน, DOM, อีเวนต์ และงานเกี่ยวกับคลิปบอร์ดได้อย่างรวดเร็วจากคอนโซล
  • บนหน้าจอที่ DOM เปลี่ยนตลอดจาก JS หรืออีเวนต์ของผู้ใช้ การหยุด JS ชั่วคราว, setTimeout(debugger), DOM snapshot และการเฝ้าดู focus มีประโยชน์มากเป็นพิเศษกับ การดีบัก HTML/CSS

ขยาย conditional breakpoint ให้เป็นเครื่องมือดีบัก

  • ใน conditional breakpoint ใส่ได้ไม่ใช่แค่เงื่อนไขจริง/เท็จ แต่ยังใส่ JavaScript expression ที่มี side effect ได้ด้วย

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

  • Logpoint และ tracepoint

    • ถ้าเรียก console.log ใน conditional breakpoint มันจะทำงานเหมือน logpoint ที่พิมพ์ค่าออกคอนโซลโดยไม่หยุดการทำงาน
    • ถ้าใช้ console.count ก็จะนับได้ด้วยว่าบรรทัดนั้นถูกรันไปกี่ครั้ง
    • ณ อัปเดตเดือนพฤษภาคม 2020 เบราว์เซอร์หลักรองรับ logpoint/tracepoint โดยตรงแล้ว
  • ใช้ Console API ใน Watch Pane

    • ใน Watch Pane ก็ใส่ expression อย่าง console.log ได้เช่นกัน
    • ทุกครั้งที่แอปพลิเคชันหยุดอยู่ที่ debugger คุณสามารถใช้ console.table(localStorage) เพื่อเก็บ snapshot ของ localStorage ได้
    • ถ้าใช้ DOM change breakpoint ร่วมกับ Watch expression ก็สามารถเก็บ snapshot หลังจาก DOM subtree เปลี่ยนไว้ในอาร์เรย์ได้
    (window.doms = window.doms || []).push(document.documentElement.outerHTML)
    
    • DOM change breakpoint ไม่สามารถตั้งให้ไม่หยุดการทำงานได้
  • ติดตาม call stack

    • ถ้ามีฟังก์ชันสำหรับแสดง loading spinner กับซ่อน spinner และคุณกำลังหาอาการที่มีแต่การเรียกแสดงค้างอยู่ คุณสามารถใส่ console.trace ลงใน conditional breakpoint ได้
    • จาก stack trace ล่าสุดของฟังก์ชันแสดง คุณสามารถคลิก caller เพื่อย้ายไปยังตำแหน่งที่เรียกใช้นั้นได้
  • เปลี่ยนพฤติกรรมโปรแกรมระหว่างรัน

    • ถ้าเขียนทับอาร์กิวเมนต์ของฟังก์ชันใน expression ของ conditional breakpoint ก็สามารถเปลี่ยน พฤติกรรมของโปรแกรม แบบสด ๆ ในเบราว์เซอร์ได้
    • เช่น คุณสามารถเปลี่ยนค่า id ของฟังก์ชัน getPerson ได้
    • expression อย่าง id=1 จะประเมินค่าเป็นจริงและทำให้ debugger หยุด ดังนั้นถ้าไม่อยากให้หยุด ให้ต่อท้ายด้วย , false
  • วัดประสิทธิภาพแบบรวดเร็ว

    • เนื่องจากเวลาในการประเมิน conditional breakpoint จะปนอยู่ด้วย จึงไม่เหมาะกับการทำ performance profiling แบบละเอียด
    • แต่ถ้าต้องการวัดแบบเร็ว ๆ ก็สามารถใส่ console.time('label') ที่เงื่อนไขจุดเริ่มต้น และ console.timeEnd('label') ที่เงื่อนไขจุดสิ้นสุด เพื่อบันทึกเวลาในการทำงานลงคอนโซลได้

ทำเงื่อนไขการหยุดให้ละเอียดขึ้น

  • ใช้จำนวนอาร์กิวเมนต์ของฟังก์ชัน

    • ถ้าต้องการหยุดเฉพาะตอนที่ฟังก์ชันปัจจุบันถูกเรียกด้วย 3 อาร์กิวเมนต์ ให้ใช้เงื่อนไขนี้
    arguments.callee.length === 3
    
    • มีประโยชน์กับ overloaded function ที่มี optional argument
    • ถ้าต้องการหยุดเฉพาะเมื่อจำนวนอาร์กิวเมนต์ที่ประกาศไว้กับจำนวนที่ส่งเข้ามาจริงไม่เท่ากัน ให้ใช้เงื่อนไขนี้
    (arguments.callee.length) != arguments.length
    
    • ช่วยหา bug ที่ตำแหน่งเรียกใช้ฟังก์ชันได้
  • เงื่อนไขตามเวลา

    • ถ้าไม่ต้องการให้หยุดก่อนผ่านไป 5 วินาทีหลังโหลดหน้า ให้ใช้เงื่อนไขนี้
    performance.now() > 5000
    
    • เหมาะเมื่อคุณสนใจ breakpoint เฉพาะหลังจากการโหลดหน้าเริ่มต้นเสร็จแล้ว
    • ถ้าต้องการให้ไม่หยุดในช่วง 5 วินาทีแรกหลังตั้ง breakpoint แต่หลังจากนั้นค่อยหยุด ให้เก็บเวลาอ้างอิงไว้ในตัวแปร global
    window.baseline = window.baseline || Date.now(),
    (Date.now() - window.baseline) > 5000
    
    • คุณสามารถรีสตาร์ตตัวนับได้จากคอนโซลด้วย window.baseline = Date.now()
  • เงื่อนไขตามสถานะ CSS

    • สามารถใช้ค่า CSS ที่คำนวณแล้วเป็นเงื่อนไขในการหยุดได้
    • เช่น ถ้าต้องการหยุดเฉพาะตอนที่สีพื้นหลังของ document.body เป็นสีแดง ให้ใช้เงื่อนไขนี้
    window.getComputedStyle(document.body).backgroundColor === "rgb(255,0,0)"
    
  • จำนวนครั้งที่รันและการสุ่มตัวอย่าง

    • สามารถเพิ่มตัวนับทุกครั้งที่บรรทัดนั้นถูกรัน เพื่อให้ หยุดแค่ทุก ๆ 2 ครั้ง ได้
    window.counter = (window.counter || 0) + 1, window.counter % 2 === 0
    
    • ถ้าต้องการสุ่มหยุดเพียง 1 ใน 10 ครั้ง ให้ใช้เงื่อนไขนี้
    Math.random() < 0.1
    
  • ไม่หยุดเด็ดขาดในบางตำแหน่ง

    • ใน Chrome ถ้าคลิกขวาที่ gutter แล้วเลือก “Never Pause Here” จะมีการสร้าง conditional breakpoint ที่เป็น false เสมอ ทำให้ไม่หยุดที่บรรทัดนั้น
    • ใช้ได้ตอนต้องการตัดบางบรรทัดออกจาก XHR breakpoint หรือเพิกเฉย exception บางรายการ
  • กำหนด instance ID อัตโนมัติ

    • คุณสามารถใส่ conditional breakpoint ใน constructor ของคลาสเพื่อเก็บแต่ละ instance ลงในอาร์เรย์ได้
    (window.instances = window.instances || []).push(this)
    
    • ID เฉพาะของ instance หนึ่ง ๆ สามารถดูได้จาก index ในอาร์เรย์
    window.instances.indexOf(instance)
    
  • สวิตช์เปิดปิด breakpoint แบบ global

    • คุณสามารถควบคุม conditional breakpoint หนึ่งจุดหรือหลายจุดด้วย global boolean ได้
    • จะเปลี่ยนค่าจากคอนโซลโดยตรง เปลี่ยนจาก breakpoint อื่น หรือเปลี่ยนด้วย timer ก็ได้
    window.enableBreakpoints = true
    setTimeout(() => (window.enableBreakpoints = true), 5000)
    

ติดตามการเรียกใช้ฟังก์ชันและคลาส

  • เฝ้าดูการเรียกใช้ method ของคลาส

    • ถ้าใช้เมธอดบรรทัดคำสั่ง monitor ของ Chrome ก็จะติดตามการเรียกใช้ method ของคลาสได้ง่าย
    • ถ้าต้องการดูการเรียก method ของทุก instance ของคลาส Dog ก็สามารถใช้ monitor กับทุกพร็อพเพอร์ตีของ prototype ได้
    var p = Dog.prototype;
    Object.getOwnPropertyNames(p).forEach((k) => monitor(p[k]));
    
    • ในคอนโซลจะพิมพ์ข้อมูลการเรียกออกมา เช่น function bark called with arguments: 2
    • ถ้าไม่อยากแค่บันทึกล็อก แต่อยากให้หยุดตอนถูกเรียกด้วย ก็ใช้ debug แทน monitor ได้
  • ติดตามคลาสจาก instance เฉพาะตัว

    • ถ้าไม่รู้ชื่อคลาสแต่มี instance อยู่ ก็สามารถเฝ้าดูได้แบบเดียวกันผ่าน prototype ของ constructor
    var p = instance.constructor.prototype;
    Object.getOwnPropertyNames(p).forEach((k) => monitor(p[k]));
    
    • มีประโยชน์เมื่ออยากสร้างฟังก์ชันที่ทำงานกับ instance ใดก็ได้โดยไม่ยึดกับคลาสเฉพาะ
  • ดีบักจากคอนโซลก่อนเรียกใช้ฟังก์ชัน

    • ก่อนจะเรียกฟังก์ชันที่ต้องการดีบักจากคอนโซล ให้รัน debugger ก่อน แล้ว Step into ไปกับการเรียกฟังก์ชันถัดไป เพื่อเข้าไปใน implementation ของฟังก์ชันได้
    debugger; fn(1);
    
    • มีประโยชน์เมื่อขี้เกียจไล่หาตำแหน่งที่ประกาศฟังก์ชัน หรือเมื่อ fn ถูก bind แบบไดนามิกจนไม่รู้ตำแหน่งในซอร์ส
    • ใน Chrome คุณยังเรียก debug(fn) เพื่อให้หยุดในฟังก์ชันทุกครั้งที่ fn ถูกเรียกได้ด้วย

ดีบักการเปลี่ยน URL และการอ่านพร็อพเพอร์ตี

  • หยุดก่อน SPA routing

    • ถ้าต้องการหยุดก่อนที่ single-page application จะเปลี่ยน URL ให้ผูกฟังก์ชันที่มี debugger เข้ากับ history.pushState, history.replaceState, window.onhashchange, window.onpopstate
    const dbg = () => {
      debugger;
    };
    history.pushState = dbg;
    history.replaceState = dbg;
    window.onhashchange = dbg;
    window.onpopstate = dbg;
    
    • เวอร์ชันของ dbg นี้ยังไม่ได้รวม implementation ที่หยุดโดยไม่ทำให้ navigation พัง
    • ในกรณีที่เรียก window.location.replace หรือ window.location.assign โดยตรง หน้าจะ unload ทันทีหลังการ assign จึงจัดการด้วยวิธีนี้ไม่ได้
    • ใน Chrome สามารถใช้ debug กับเมธอดที่เกี่ยวข้องเพื่อดู source ของ redirect และสถานะในจังหวะนั้นได้
    debug(window.location.replace);
    debug(window.location.assign);
    
  • หาให้เจอว่ามีการอ่านพร็อพเพอร์ตีตรงไหน

    • ถ้าต้องการรู้จังหวะที่มีการอ่าน property ใด property หนึ่งของออบเจ็กต์ สามารถใส่ debugger ไว้ใน getter ได้
    { get configOption() { debugger; return true; } }
    
    • มีประโยชน์เมื่อต้องการดูว่าโค้ดใดใช้ option การตั้งค่านั้นอย่างไร
    • จะปรับจากซอร์สต้นฉบับโดยตรง หรือใช้ conditional breakpoint เพื่อทำแบบเดียวกันก็ได้

คัดลอกข้อมูลจากคอนโซล

  • ถ้าใช้ Console API copy() คุณจะคัดลอกข้อมูลจากเบราว์เซอร์ไปยัง คลิปบอร์ด ได้โดยไม่โดนตัดสตริง
  • ใช้ได้กับการคัดลอก DOM snapshot ปัจจุบัน, metadata ของ resource อย่างรูปภาพ, JSON blob ขนาดใหญ่, และ dump ของ localStorage
copy(document.documentElement.outerHTML)
copy(performance.getEntriesByType("resource"))
copy(JSON.parse(blob))
copy(localStorage)

การดีบัก HTML/CSS

  • ตรวจ DOM ขณะ JS หยุดอยู่

    • ใน DOM inspector ของ Chrome/Windows ถ้ากด ctrl+\ ก็จะหยุดการทำงานของ JS ได้ทุกเมื่อ
    • แม้ในสถานการณ์ที่ JS เปลี่ยน DOM หรืออีเวนต์อย่าง mouseover เปลี่ยน DOM ก็ยังตรวจดู DOM snapshot ปัจจุบัน ได้อย่างเสถียร
  • ตรวจองค์ประกอบที่หายไป

    • องค์ประกอบ DOM แบบมีเงื่อนไขที่หายไปเมื่อขยับเมาส์ สามารถตรวจได้โดยตั้ง timer 5 วินาทีในคอนโซลให้รัน debugger
    setTimeout(function() { debugger; }, 5000);
    
    • เมื่อเปิด UI ทิ้งไว้ 5 วินาทีแล้ว JS หยุดทำงาน องค์ประกอบจะไม่หายไป จึงย้ายไปที่เครื่องมือสำหรับนักพัฒนาเพื่อตรวจได้
    • ระหว่างที่ JS หยุดอยู่ คุณสามารถตรวจ element, แก้ CSS และสั่งคำสั่ง JS จากคอนโซลได้
    • มีประสิทธิภาพมากเมื่อจะตรวจ DOM ที่ขึ้นกับตำแหน่งเคอร์เซอร์หรือ focus
  • บันทึก DOM snapshot

    • ถ้าต้องการคัดลอก DOM ปัจจุบัน ให้ใช้คำสั่งนี้
    copy(document.documentElement.outerHTML);
    
    • ถ้าต้องการเก็บสตริง DOM ลงอาร์เรย์ทุก 1 วินาที ก็ใช้ setInterval แบบนี้
    doms = [];
    setInterval(() => {
      const domStr = document.documentElement.outerHTML;
      doms.push(domStr);
    }, 1000);
    
    • หรือจะไม่เก็บลงอาร์เรย์ แต่พิมพ์ออกคอนโซลเรื่อย ๆ ก็ได้
  • เฝ้าดู element ที่ได้รับ focus

    • สามารถเทียบ document.activeElement เป็นช่วง ๆ แล้วพิมพ์การเปลี่ยน focus ออกคอนโซลได้
    (function () {
      let last = document.activeElement;
      setInterval(() => {
        if (document.activeElement !== last) {
          last = document.activeElement;
          console.log("Focus changed to: ", last);
        }
      }, 100);
    })();
    
  • หา element ที่เป็นตัวหนา

    • สามารถหา element ที่ fontWeight ที่คำนวณแล้วเป็น "bold" หรือ "700" ได้จากทุก element
    const isBold = (e) => {
      let w = window.getComputedStyle(e).fontWeight;
      return w === "bold" || w === "700";
    };
    Array.from(document.querySelectorAll("*")).filter(isBold);
    
    • ถ้าต้องการค้นหาเฉพาะในลูกหลานของ element ที่เลือกอยู่ใน inspector ตอนนี้ ก็ใช้ $0 เป็นจุดอ้างอิงได้
    Array.from($0.querySelectorAll("*")).filter(isBold);
    
  • จัดการ element ที่เลือกอยู่และอีเวนต์

    • $0 ในคอนโซลคือการอ้างอิงอัตโนมัติไปยัง element ที่เลือกอยู่ใน Element Inspector
    • ใน Chrome และ Edge สามารถเข้าถึง element ที่เคยตรวจไว้ก่อนหน้านั้นได้ผ่าน $1, $2 เป็นต้น
    • ใน Chrome สามารถดู event listener ของ element ที่เลือกอยู่ตอนนี้ได้
    getEventListeners($0)
    
    • ถ้าต้องการดีบักทุกอีเวนต์ของ element ที่เลือกอยู่ ให้ใช้ monitorEvents($0)
    • ถ้าต้องการดูเฉพาะบางกลุ่มอีเวนต์ ก็ส่งอาร์เรย์เข้าไปได้
    monitorEvents($0, ["control", "key"])
    

1 ความคิดเห็น

 
GN⁺ 2023-11-12
ความคิดเห็นจาก Hacker News
  • ตลอดหลายทศวรรษที่ผ่านมา เครื่องมือดีบัก ที่ฝังมาในเบราว์เซอร์พัฒนาไปมากจริง ๆ
    ในฐานะคนที่ใช้ JS มานาน รู้สึกขอบคุณอย่างมากต่อคนที่ทำให้การดีบักโค้ดในเบราว์เซอร์เป็นเรื่องที่เข้าใจง่ายได้ขนาดนี้
    พอไปทำงานฝั่งแบ็กเอนด์หรือสายพัฒนาอื่น ๆ ก็จะคิดถึงระบบนิเวศของเครื่องมือดีบักที่เบราว์เซอร์สมัยใหม่ให้มานี้

    • อยากปรบมือให้คนที่สร้าง ส่วนขยาย Firebug ด้วย ซึ่งเป็นตัวผลักดันสำคัญที่ยกระดับเครื่องมือสำหรับนักพัฒนาในเบราว์เซอร์อย่างจริงจัง
    • อยากรู้ว่าอะไรในการดีบัก JS ที่ทำให้รู้สึกว่ามันพิเศษขนาดนั้น
      ฉันดีบัก Python, Java, C++ มาเยอะหลายปี แต่ไม่เคยรู้สึกว่าเครื่องมือมันขาดอะไร
      แน่นอนว่าเคยเห็นคนจำนวนมากที่ไม่รู้ว่ามีตัวเลือกอะไรให้ใช้ในสายนี้บ้าง
    • แม้จะไม่สมบูรณ์แบบ แต่ก็ยังดีกว่าประสบการณ์ ดีบัก Node.js ด้วย VS Code ที่เพื่อนร่วมงานคนหนึ่งพยายามเชียร์มาก
      เจอแบบนั้นฉันยอมเลือกคอนโซลดีกว่า
  • setTimeout(function() { debugger; }, 5000); นี่ฉลาดมาก
    สุดท้ายแล้ววิธีเดียวที่จะชนะสแตกเต่าเรียกซ้อนแบบรีเคอร์ซีฟที่ Chrome debugger ใช้ดีบักตัวเองได้ ก็คือ คำสั่ง debugger เท่านั้น
    sam.pl ของ Myspace Sammy worm อันลือลั่นก็ใช้กับดักดีบักเพื่อกันไม่ให้ผู้เยี่ยมชมไปขุดหน้าโฮมเพจ HTML ที่ถูกทำให้อ่านยาก

    • นี่เป็นหนึ่งใน bookmarklet ที่ใช้บ่อย: https://darekkay.com/blog/debugging-dynamic-content/
    • เรื่องแบบนี้เกิดขึ้นตอนเว็บไซต์บล็อก Console/DevTools side panel ที่เปิดอยู่
      มันจะวนลูปหลักแล้วแทรกคำสั่ง debugger; ที่น่ารำคาญไว้ทั่ว ๆ ไป พร้อมรันที่ช่วง 30FPS / 32ms เพื่อทำให้ DevTools ใช้งานไม่ได้
      เพราะไม่มีทาง “เพิกเฉย” ต่อคำสั่ง debugger ได้
    • ถ้าไม่อยากรอ 5 วินาที ก็ใช้คีย์ลัด หยุดการรันสคริปต์ชั่วคราว cmd-\ ได้
      หน้าจะหยุดที่บรรทัด JS ที่กำลังรันอยู่ แล้วคุณก็ตรวจสอบ element ที่มีปัญหาได้ตามปกติ
    • อยากให้ผู้พัฒนาเบราว์เซอร์ทำตัวเลือกใน DevTools ที่เปิดเองอย่างชัดเจน และตั้งค่าเริ่มต้นให้ เพิกเฉยต่อคำสั่ง debugger
      กรณีใช้งานคำสั่ง debugger แบบปกติที่ถูกต้องส่วนใหญ่ ถ้าคุณกำลังทำงานกับโค้ดของตัวเองอยู่ ก็แทนด้วย breakpoint ธรรมดาได้
    • เดี๋ยวก่อน มีแค่นี้เองเหรอ? มันเรียบง่ายเกินไปแต่ดีมาก
  • สังเกตว่าขาด queryObjects ไป
    มันเป็น API ที่ค่อนข้างบ้าดี ซึ่งจะคืนรายการออบเจ็กต์ทั้งหมดที่สร้างจาก constructor ที่กำหนด
    ตัวอย่างเช่น queryObjects(Function) จะดึงรายการฟังก์ชันทั้งหมดใน heap ออกมาได้
    มันคืนค่าได้แม้กระทั่งฟังก์ชัน “private” ที่อยู่ในบางโมดูล

    • พูดตามตรง ต่อให้ไม่อยากเผยแพร่เรื่องนี้ก็ดูสมเหตุสมผล
      มันเป็นฟีเจอร์ที่บ้ามากจริง ๆ และน่าจะเป็น Chromium เท่านั้น
  • ฉันใช้งาน watch variable ไม่เคยได้เรื่องเลย
    กฎเรื่องสโคปและการอัปเดต มันคลุมเครือเกินไป จนเหมือนว่าดูได้แค่ตัวแปร global แต่ถึงอย่างนั้นก็ยังไม่ทำงานตามที่คาด เลยลงเอยด้วยการถม log ค่าเต็มไปหมดเวลาทดสอบ
    ฉันคิดมาหลายปีแล้วว่าควรมี UI สไตล์ Data.gui [1] ในคอนโซล เพื่อให้ดูและทดสอบค่าตัวแปรกับค่าคอนฟิกได้
    ดูการทำงานจริงได้ใน CodePen [2] นี้

    1. https://github.com/dataarts/dat.gui
    2. https://codepen.io/russellbeattie/full/kGxaqM
    • ฉันก็รู้สึกหงุดหงิดแบบเดียวกัน
      ในทางทฤษฎีเบราว์เซอร์มีฟีเจอร์ดีบักที่ยอดเยี่ยมเยอะมาก แต่ในทางปฏิบัติมันดูไม่ค่อยทำงานได้เสถียร
      แม้แต่ breakpoint ก็ยังทำให้ทำงานได้เสถียรทั้งหมดไม่ได้
      ตอนโค้ดไม่ถูก bundle ก็โอเค แต่พอ bundle เมื่อไร ต่อให้ยังไม่ minify ก็ดูเหมือนว่าฟีเจอร์ดีบักหลายอย่างจะพัง
      ฉันไม่ใช่วิศวกรฟรอนต์เอนด์ เลยอาจกำลังทำอะไรผิดอยู่ก็ได้
    • น่าจะลองปิด source map ใน debugger ดู
      มันน่าจะเป็นสาเหตุของความหงุดหงิดนี้ และฉันก็ยังไม่เข้าใจว่าทำไมประสบการณ์ดีบักแบบเปิดใช้งานมันอยู่ถึงแย่ขนาดนี้ แต่กลับถูกเปิดเป็นค่าเริ่มต้น
    • ในแถบ side panel ของ Chrome ตัวแปรที่ถูกย่อชื่อยังแสดงเป็นชื่อที่ถูกต้อง แต่ใน watchpoint กลับขึ้น error ว่าไม่ได้ถูกกำหนด เลยน่าหงุดหงิดมาก
  • น่าจะดีถ้ามีวิธีเข้าถึง ตัวแปรภายในของ IIFE โดยไม่ต้องหยุดการทำงานของโค้ดในสโคป IIFE
    มีวิธีโน้มน้าวดีบักเกอร์ให้ทำแบบนั้นได้ไหม?

    • IIFE คือ Immediately Invoked Function Expression
      https://en.m.wikipedia.org/wiki/Immediately_invoked_function...
    • ถ้าเข้าเบรกพอยต์ในฟังก์ชันที่ IIFE เรียก ก็แค่ ไล่ย้อนขึ้นไปตามสแตก
      แต่ถ้าอยู่นอก IIFE โดยสมบูรณ์ก็คงทำไม่ได้
      ตัวแปรที่กำลังหาอาจไม่มีอยู่ในหน่วยความจำทั้งก่อนหรือหลังการทำงาน
      JavaScript ไม่มีตัวแปรแบบสแตติกเหมือนภาษาอื่น ดังนั้นทุกครั้งที่ IIFE ถูกเรียก ตัวแปรภายในก็จะถูกทิ้งไป
      JavaScript มีโมเดลแบบเธรดเดี่ยวที่ค่อนข้างเข้มแข็ง เว้นแต่จะใช้สิ่งอย่าง web worker ดังนั้นถ้าไม่ได้พยายามสร้าง race condition ด้วยการเรียก async/await โดยตรง แนวคิดที่ว่าตัวแปรแบบนั้นจะยังค้างอยู่ในหน่วยความจำนอกสโคป IIFE ก็แทบจะไม่成立
      ถ้าใช้ var แบบพร่ำเพรื่อแล้วกำหนดค่าเริ่มต้นตัวแปรนอกสโคป IIFE ก็อาจเห็นค่าที่การเรียก IIFE ครั้งล่าสุดสร้างไว้ได้
    • เมื่อราว 12 ปีก่อนฉันเคยสร้างเครื่องมือที่ทำเรื่องแบบนั้น
      ต้องปรับแก้นิดหน่อย แต่คุ้มกับความพยายามที่ลงไป
      https://github.com/kristopolous/_inject
      มันสามารถไล่ดูบริบทของฟังก์ชันแทบใดก็ได้ในช่วงเวลาที่ต้องการเพื่อดูว่าเกิดอะไรขึ้น
      วิธีคืออาศัยการใช้ reference counter ในทางที่ผิดเพื่อไม่ให้คอนเท็กซ์ถูกทำลาย
      ตอนที่ทำ client-side JS เยอะ ๆ มันมีประโยชน์มาก
      เวอร์ชัน killer app ที่ทำสิ่งนี้อย่างสมบูรณ์น่าจะอยู่ในรูปแบบการเปิด REPL ได้จากทุกคอนเท็กซ์
      แต่แม้ในสภาพตอนนี้ ถ้าจะใช้งานให้ดีจริง ๆ ก็ต้องชำนาญพอสมควร
    • สิ่งที่กำลังหาคือ logpoint หรือเปล่า?
      https://firefox-source-docs.mozilla.org/devtools-user/debugg...
      แก้ไข: เพิ่งสังเกตว่าในบทความที่ลิงก์มาพูดถึงเรื่องนี้เป็นอย่างแรกแบบตรงตัวเลย
    • ใน Chrome ให้ตรวจดู closure ที่อธิบายไว้ใน https://news.ycombinator.com/item?id=38226743#38231705 ผ่าน แผง Watch แล้วดูพร็อพเพอร์ตีเทียม [[Scopes]]
      ดูเหมือน Firefox จะไม่มีวิธีแบบนี้
  • เพื่อความครบถ้วน ขอแนะนำ Werkzeug
    ฉันใช้กับการพัฒนาแบ็กเอนด์ Django และมันมีประโยชน์มาก
    มันทำให้สามารถใช้เชลล์ “PDB” ในเบราว์เซอร์ได้ทุกที่ทุกเวลาเมื่อเกิด exception

    • ทำได้ง่าย แค่ใส่ 1/0 ตรงจุดที่อยากให้ Werkzeug โผล่ขึ้นมาในเบราว์เซอร์
    • อยากได้ประสบการณ์แบบนั้นทุกที่ รวมถึงใน nose.js และเบราว์เซอร์
      มันเรียบง่ายและตรงไปตรงมามาก
    • ยังจำเป็นต้องใช้ django-extension/runserver_plus อยู่ไหม?
  • เคล็ดลับที่ฉันใช้ประจำคือ ค้นหาสคริปต์จากสตริง UI ที่โหลดมา แล้วค่อยดีบัก
    ไปที่แผง Network แล้วเริ่มบันทึกคำขอเครือข่าย เปิดแถบด้านซ้าย จากนั้นค้นหาโค้ดหรือสตริง UI ที่ต้องการ
    ปกติมักจะเจอในไฟล์ JS bundle chunk แปลก ๆ แล้วเมื่อคลิกผลลัพธ์ก็จะเปิดคำขอเครือข่ายของไฟล์นั้น
    คลิกขวาตรงไหนก็ได้ในไฟล์นั้นแล้วเลือกเมนูประมาณ “Open in Sources” เพื่อกระโดดไปยังดีบักเกอร์
    จากนั้นก็วางคำสั่ง debugger ได้เลย และระหว่างทาง source map ก็น่าจะถูกโหลดด้วย

  • ฉันทำงานเป็นโปรแกรมเมอร์ Python/Elixir มานาน และใช้ pdb.set_trace() กับ IEx.pry() บ่อยมาก
    ไม่นานมานี้ฉันรับช่วงต่อ แบ็กเอนด์ NodeJS ที่ยุ่งเหยิงมากตัวหนึ่ง และกำลังดึงผมที่เหลืออยู่เพราะไม่มีเครื่องมือดีบักที่เหมาะสม
    สุดท้ายก็ต้องกลับไปดีบักด้วย console.log ซึ่งให้ความรู้สึกเหมือนมนุษย์ถ้ำ
    ไม่น่าเชื่อว่า ecosystem ที่ได้รับความนิยมขนาดนี้จะไม่มี REPL สำหรับดีบักที่ดีพอ มีใครพอบอกได้ไหมว่าควรไปทางไหน?

    • มี
      ใช้ node --inspect-brk แล้วเชื่อมต่อผ่าน VS Code หรือ Chrome DevTools ได้
      ส่วนที่จุกจิกคือมีเครื่องมือ build อย่างการแปลง TypeScript วางอยู่หน้าคำสั่ง nodejs หรือไม่
      ถ้ารัน node ตรง ๆ ก็ถือว่าค่อนข้างง่าย
      REPL สำหรับดีบักก็คือ JavaScript console นั่นเอง
    • ถ้าใช้เครื่องมือสำหรับนักพัฒนาใน VSCode ก็สามารถดีบัก NodeJS ได้ค่อนข้างดี
      เพิ่มเป็นตัวเลือกดีบักแล้วจะวางเบรกพอยต์และ step เข้าไปในฟังก์ชันได้
      เพราะส่วนใหญ่ถูกห่ออยู่ในอ็อบเจ็กต์ JavaScript จึงถือว่าดีบักได้ค่อนข้างดี
    • อย่างที่คนอื่นบอก สิ่งที่ต้องการคือ --inspect หรือ --inspect-brk
      ใน VSCode ยังใช้คำสั่ง “open JavaScript debug terminal” ได้ด้วย
      คำสั่งนี้จะเปิดเทอร์มินัลที่ทำให้คำสั่ง node ใด ๆ เริ่มต้นมาพร้อมดีบักเกอร์ที่เชื่อมต่อโดยอัตโนมัติ
    • อาจยังไม่พอ แต่ฉันเพิ่งไปรู้จัก python snoop
      https://pypi.org/project/snoop/
      เป็น decorator สำหรับการไล่ติดตามแบบลึก
    • สิ่งที่ดีใน Elixir REPL ซึ่งดูคล้ายกับ Erlang REPL คือ สามารถเชื่อมต่อกับโปรเซสที่กำลังรันอยู่
      ฉันยังไม่เห็นความสามารถแบบนี้ใน ecosystem ของ Node JS
  • ในส่วน “Debugging Property Reads” ฉันสงสัยว่าจะใช้ conditional breakpoint เพื่อเปลี่ยน {configOption: true} ให้เป็น {get configOption() { debugger; return true; }} ได้อย่างไร

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

    • แนะนำให้ตามซีรีส์ “What’s new in DevTools” ของทีม Chrome มาก ๆ
      ตอนที่กำลังทำงานอยู่ อาจไม่ค่อยอยากกดลิงก์ไปอ่าน release notes แต่แค่ใช้เวลาไล่ดู 5 นาทีทุกครั้งที่มีเวอร์ชันใหม่ออกมาก็คุ้มแล้ว
      ยังมีวิดีโอสั้น ๆ ไม่กี่นาทีที่ดูง่าย เหมาะสำหรับเก็บภาพรวมแบบเร็ว ๆ
      แม้จุดประสงค์จะเป็นการแนะนำฟีเจอร์ใหม่ แต่จากประสบการณ์ก็มักช่วยให้เข้าใจข้อจำกัดปัจจุบันของเครื่องมือด้วย