ข้ามไปยังเนื้อหา

JavaScript เบื้องต้น

Node.js คือ runtime ที่รัน JavaScript นอก browser ทุกอย่างที่เขียนใน Node ตั้งแต่ HTTP server ง่ายๆ ไปจนถึง CLI tool ที่ซับซ้อน ล้วนเป็น JavaScript ดังนั้นการเข้าใจภาษาอย่างแน่นหนาจึงเป็นสิ่งจำเป็นก่อนที่จะลงลึกใน API ของ Node

โมดูลนี้ครอบคลุม JavaScript พื้นฐานที่นักพัฒนามักสะดุดมากที่สุดในระบบนิเวศ Node:

บทเรียนหัวข้อ
Types และ CoercionPrimitives vs objects, typeof, == vs ===, truthy/falsy
Scope และ Closuresvar/let/const, block scope, hoisting, lexical scope
this และ ContextDynamic this, call/apply/bind, arrow functions
ModulesESM import/export vs CommonJS require, ใช้เมื่อไหร่

JavaScript สมัยใหม่หมายถึง ES2022 ขึ้นไป ตลอดทั้งโมดูล — const/let, arrow functions, destructuring, async/await, optional chaining และ native ES modules โมดูลนี้ยึด Node 24 (LTS ตัวปัจจุบัน) และทุกอย่างรันบน Node 26 ได้เช่นกัน

โค้ดด้านล่างแสดงแนวคิด 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));
}
JavaScript

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 arithmetic
const 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 throw

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), หรือเขียนโค้ด ภายใน class body (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"]
อะไรเป็นตัวกำหนด sloppy vs strict mode

นี่คือเหตุผลที่ 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) rejects
function setTotal() {
total = 42; // no const/let/var
}
setTotal();
console.log(total); // sloppy mode: 42 — an implicit global was created
JavaScript

ให้นึกภาพ 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 เก่า

ModeBenefitCost
Sloppy (legacy default)สคริปต์เก่ายังรันได้โดยไม่ต้องแก้typo กลายเป็นตัวแปร global ที่มองไม่เห็นแทนที่จะเป็น error; this ในการเรียกแบบธรรมดากลายเป็น global object แบบเงียบๆ
Strict (ESM, class, 'use strict')ความผิดพลาดปรากฏทันทีเป็น ReferenceError/TypeError ตรงจุดที่เกิด bugpattern เก่าบางตัว (arguments บางแบบ, with) ใช้ไม่ได้ — แทบไม่มีผลกับโค้ดสมัยใหม่
  • “'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 โดยไม่มีเงื่อนไขอยู่แล้ว

โค้ดที่ 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 cache
store[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 ไหน

Node.js ให้สิ่งที่ browser ไม่มีคืออะไร?
โมดูลนี้พึ่งพาฟีเจอร์ JavaScript เวอร์ชันใด?
ใน 3 context นี้ context ไหนที่ strict mode **ไม่ได้** เป็นอัตโนมัติ และต้องมี directive 'use strict'; ชัดเจนถึงจะได้?
โค้ดนี้พิมพ์อะไรเมื่อรันเป็นสคริปต์ CommonJS แบบ sloppy ธรรมดา (ไม่มี 'use strict')?
function setTotal() {
  total = 42;
}
setTotal();
console.log(total);
ทำไมฟังก์ชันนี้ถึงทำงานผิดเงียบๆ ถ้าถูกย้ายไปอยู่ใน ES module?
function cache(key, value) {
  if (!store) store = {};
  store[key] = value;
  return store;
}
cache('a', 1);