JavaScript เบื้องต้น
JavaScript คือภาษาของ Node.js
หัวข้อที่มีชื่อว่า “JavaScript คือภาษาของ Node.js”Node.js คือ runtime ที่รัน JavaScript นอก browser ทุกอย่างที่เขียนใน Node ตั้งแต่ HTTP server ง่ายๆ ไปจนถึง CLI tool ที่ซับซ้อน ล้วนเป็น JavaScript ดังนั้นการเข้าใจภาษาอย่างแน่นหนาจึงเป็นสิ่งจำเป็นก่อนที่จะลงลึกใน API ของ Node
โมดูลนี้ครอบคลุม JavaScript พื้นฐานที่นักพัฒนามักสะดุดมากที่สุดในระบบนิเวศ Node:
| บทเรียน | หัวข้อ |
|---|---|
| Types และ Coercion | Primitives vs objects, typeof, == vs ===, truthy/falsy |
| Scope และ Closures | var/let/const, block scope, hoisting, lexical scope |
this และ Context | Dynamic this, call/apply/bind, arrow functions |
| Modules | ESM import/export vs CommonJS require, ใช้เมื่อไหร่ |
JavaScript สมัยใหม่หมายถึง ES2022 ขึ้นไป ตลอดทั้งโมดูล — const/let, arrow functions, destructuring, async/await, optional chaining และ native ES modules โมดูลนี้ยึด Node 24 (LTS ตัวปัจจุบัน) และทุกอย่างรันบน Node 26 ได้เช่นกัน
Demo แรกที่รันได้
หัวข้อที่มีชื่อว่า “Demo แรกที่รันได้”โค้ดด้านล่างแสดงแนวคิด JS หลายอย่างพร้อมกัน: การประกาศ const, ฟังก์ชัน, การต่อสตริงแบบไม่ใช้ template literal และ closure เหนือตัวแปรภายนอก ลองรันและสังเกตผลลัพธ์
const greet = function(name) { return 'Hello, ' + name + '!';};
const languages = ['JavaScript', 'TypeScript', 'Node.js'];
for (const lang of languages) { console.log(greet(lang));}ฟีเจอร์ภาษาใหม่ ๆ ที่คุณจะได้เจอ
หัวข้อที่มีชื่อว่า “ฟีเจอร์ภาษาใหม่ ๆ ที่คุณจะได้เจอ”Node อัปเกรด V8 ทุก major release ภาษาจึงโตขึ้นเรื่อย ๆ:
- Node 24 —
using/await usingสำหรับเคลียร์ resource อัตโนมัติ,RegExp.escape(),Error.isError(),Float16Array - Node 26 — API วันเวลา Temporal เปิดเป็นค่าเริ่มต้น พร้อม
Map.prototype.getOrInsert()และIterator.concat()
// @skip-verify Node 26-only (Temporal) + illustrative openConnection()// Node 26: Temporal replaces error-prone Date arithmeticconst due = Temporal.Now.plainDateISO().add({ days: 30 });console.log(due.toString()); // e.g. 2026-10-12
// Node 24: using — the resource is disposed when the block exits{ using conn = openConnection(); // the object has a [Symbol.dispose]() method conn.query('SELECT 1');} // conn[Symbol.dispose]() runs here, even on throwScript mode: sloppy vs strict
หัวข้อที่มีชื่อว่า “Script mode: sloppy vs strict”JavaScript ทุกชิ้นรันอยู่ใน mode ใดมode หนึ่งจากสองแบบ ตัดสินโดย วิธีที่ไฟล์ถูกโหลด ไม่ใช่โดยสิ่งที่โค้ดทำ:
- Sloppy mode — ค่า default ดั้งเดิม
<script>tag แบบคลาสสิกใน browser หรือไฟล์.jsใน package ที่ไม่มี"type": "module"จะรัน sloppy เว้นแต่จะ opt out sloppy mode ให้อภัยความผิดพลาดที่ปกติจะเป็น bug: การ assign ให้ชื่อที่ไม่เคยถูกประกาศจะสร้างตัวแปร global ขึ้นมาเงียบๆ แทนที่จะ throw และการเรียกฟังก์ชันแบบธรรมดาจะตั้งthisเป็น global object แทนที่จะเป็นundefined - Strict mode — เปลี่ยนความให้อภัยแบบเงียบๆ เหล่านั้นให้กลายเป็น error ที่ throw ออกมา คุณจะได้ strict mode สามทาง: เขียน
'use strict';เป็น statement แรกสุดในสคริปต์หรือฟังก์ชัน, โหลดไฟล์เป็น ES module (.mjsหรือ.jsที่มี"type": "module"— strict เสมอ ไม่ต้องมี directive), หรือเขียนโค้ด ภายในclassbody (class body เป็น strict เสมอแม้อยู่ในไฟล์ที่ปกติเป็น sloppy)
flowchart TD A["How is this code loaded?"] --> B["Classic script / CommonJS .js file"] A --> C["ES module: .mjs, or .js with package.json type: module"] A --> D["Any code inside a class body"] B -->|"no directive"| E["Sloppy mode"] B -->|"'use strict' as first line"| F["Strict mode"] C --> F D --> F E --> G["Undeclared assignment creates a global; this === global object in a plain call"] F --> H["Undeclared assignment throws ReferenceError; this === undefined in a plain call"]
นี่คือเหตุผลที่ demo ของ this และ Context ในบทถัดไปของโมดูลนี้เปิดด้วย 'use strict'; — ถ้าไม่มี การเรียก method แบบ detached จะ fail อย่างเงียบๆ โดยแก้ไข object ผิดตัวแทนที่จะ throw error ที่คุณเห็นและแก้ได้
// @skip-verify illustrative — contrasts what a plain (sloppy) script allows// vs what strict mode (any ES module, any class, or an explicit directive) rejectsfunction setTotal() { total = 42; // no const/let/var}setTotal();console.log(total); // sloppy mode: 42 — an implicit global was createdMental Model
หัวข้อที่มีชื่อว่า “Mental Model”ให้นึกภาพ sloppy mode เป็น JavaScript ที่รันพร้อม safety net ที่ผ่อนปรน: typo และการประกาศตัวแปรที่ขาดหายจะถูกดูดซับเงียบๆ แทนที่จะหยุดโปรแกรม Strict mode เอา net นั้นออก มันไม่ได้เพิ่มฟีเจอร์ใหม่ — มันเอา silent failure path ออก แล้วเปลี่ยนเป็น error ที่ throw ตรงบรรทัดที่ความผิดพลาดเกิดขึ้นจริง เพราะ ES module และ class เป็น strict โดยไม่มีเงื่อนไข และโค้ด Node.js ใหม่แทบทั้งหมดเขียนเป็น ES module คุณควร default ไปที่การ สมมติ ว่าเป็น strict mode และมอง sloppy mode เป็นข้อยกเว้นแบบ legacy ที่คุณอาจเจอบ้างในไฟล์ CommonJS เก่า
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| Mode | Benefit | Cost |
|---|---|---|
| Sloppy (legacy default) | สคริปต์เก่ายังรันได้โดยไม่ต้องแก้ | typo กลายเป็นตัวแปร global ที่มองไม่เห็นแทนที่จะเป็น error; this ในการเรียกแบบธรรมดากลายเป็น global object แบบเงียบๆ |
Strict (ESM, class, 'use strict') | ความผิดพลาดปรากฏทันทีเป็น ReferenceError/TypeError ตรงจุดที่เกิด bug | pattern เก่าบางตัว (arguments บางแบบ, with) ใช้ไม่ได้ — แทบไม่มีผลกับโค้ดสมัยใหม่ |
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “
'use strict'เป็นของเก่าจาก ES5 ที่ไม่มีใครต้องใช้แล้ว” ยังต้องใช้อยู่ในไฟล์ CommonJS.js/.cjsธรรมดาถ้าต้องการ strict guarantee ที่นั่น — แต่ได้มันมาฟรีๆ โดยไม่ต้องมี directive ใน ES module และ class ทุกตัว - “Strict mode เปลี่ยนสิ่งที่โค้ดคำนวณ” มันไม่ได้เพิ่มหรือเปลี่ยน semantics ของ arithmetic หรือ object มันแค่เปลี่ยนความผิดพลาดที่เคยเงียบให้กลายเป็น error ที่ throw
- “ถ้าไฟล์มี
"type": "module"ในpackage.jsonก็ยังต้องใส่'use strict'ไว้บนสุดอยู่ดี” ไม่จำเป็น — directive นั้นซ้ำซ้อนตรงนั้น เพราะ ESM เป็น strict โดยไม่มีเงื่อนไขอยู่แล้ว
Reading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”โค้ดที่ AI สร้างมักถูกเขียนและทดสอบเป็นสคริปต์ CommonJS สั้นๆ (sloppy โดย default) แล้วสมมติว่าจะทำงานเหมือนเดิมทุกประการเมื่อย้ายไปอยู่ใน class หรือ ES module ให้ระวังโค้ดที่พึ่งพาการ assign แบบ “ทำงานได้เฉยๆ” โดยไม่มี const/let/var นำหน้า — นั่นคือ bug แฝงที่ context แบบ strict เท่านั้น (ESM, class หรือ directive ชัดเจน) จะจับให้คุณเห็นจริงๆ
🐛 หา bug ในโค้ดนี้
function cache(key, value) {if (!store) store = {}; // no declaration — meant to be a shared cachestore[key] = value;return store;}
cache('a', 1);cache('b', 2);console.log(store); // "works" by accident todayเฉลย
store ไม่เคยถูกประกาศด้วย const/let/var ใน CommonJS script แบบ sloppy สิ่งนี้จะสร้างตัวแปร global ชื่อ store เงียบๆ ในครั้งแรกที่ cache รัน ซึ่งเป็นเหตุผลที่มัน “ทำงานได้” — ทุกการเรียกบังเอิญใช้ global ตัวเดียวกันร่วมกัน ย้ายฟังก์ชันเดียวกันนี้ไปอยู่ใน ES module, class method หรือเพิ่ม 'use strict' เข้าไปในไฟล์ แล้วการเรียกครั้งแรกจะ throw ReferenceError: store is not defined แทน วิธีแก้คือประกาศตัวแปรใน scope ที่ครอบคลุมอยู่ (const store = {}; ระดับ module) เพื่อไม่ให้พฤติกรรมขึ้นอยู่กับว่าไฟล์รันใน mode ไหน