Skip to content

บทที่ 02 — ตัวแปร และ ชนิดข้อมูล

← บทที่ 01 | สารบัญ | บทที่ 03: Operators →

บทนี้คือรากฐานของทุกอย่าง — ตัวแปร (variable) คือ "กล่องเก็บข้อมูล" และชนิดข้อมูล (type) คือ "ชนิดของสิ่งที่เก็บ"

อ่านจบบทนี้คุณจะ:

  • เข้าใจ let, const, var — ใช้อันไหนเมื่อไหร่ และทำไม
  • รู้จักชนิดข้อมูลทั้งหมดของ JavaScript
  • เข้าใจความต่างที่สำคัญมากระหว่าง "primitive" กับ "object"
  • เข้าใจการแปลงชนิดข้อมูล (ที่เป็นต้นตอของบั๊กเยอะ)

ใช้เวลา 2-3 ชั่วโมง — อ่านช้า ๆ ลองโค้ดตาม


Part 1: ตัวแปรคืออะไร

1.1 แนวคิด

ตัวแปร (variable) = กล่องที่มีชื่อ ไว้เก็บข้อมูล

ลองนึกภาพตู้เก็บของที่มีป้ายชื่อ — เราเอาของใส่กล่อง ติดป้าย แล้วเรียกใช้ของผ่านป้ายชื่อ

javascript
let age = 25;

บรรทัดนี้แปลว่า: "สร้างกล่องชื่อ age แล้วใส่เลข 25 ลงไป"

ต่อมาเราเรียกใช้ค่าผ่านชื่อ age:

javascript
console.log(age);          // 25
console.log(age + 5);      // 30

1.2 ทำไมต้องมีตัวแปร

ลองนึกถ้าไม่มีตัวแปร — เราต้องเขียนค่าตรง ๆ ทุกครั้ง:

javascript
console.log("ราคา 100 บาท ลด 10% เหลือ " + (100 - 100 * 0.1));

ถ้าราคาเปลี่ยน ต้องแก้ทุกที่ที่มีเลข 100 — พลาดง่าย

ใช้ตัวแปร — แก้ที่เดียว:

javascript
let price = 100;
let discount = 0.1;
console.log("ราคา " + price + " บาท ลด " + (discount * 100) + "% เหลือ " + (price - price * discount));

Part 2: 3 คำสั่งประกาศตัวแปร — let, const, var

JavaScript มี 3 คำสำหรับ "สร้างตัวแปร" — และนี่คือเรื่องที่มือใหม่ต้องเข้าใจให้ชัด

javascript
let x = 10;       // ตัวแปรที่เปลี่ยนค่าได้
const y = 20;     // ตัวแปรที่เปลี่ยนค่าไม่ได้
var z = 30;       // วิธีเก่า — เลิกใช้

2.1 const — ใช้เป็นค่าเริ่มต้น (default)

const ย่อมาจาก "constant" (ค่าคงที่) — ตัวแปรที่ ตั้งค่าแล้วเปลี่ยนไม่ได้

javascript
const pi = 3.14159;
pi = 3;            // ❌ TypeError: Assignment to constant variable
                   //    (กำหนดค่าใหม่ให้ตัวแปร const ไม่ได้)

ถามว่า — ถ้าเปลี่ยนค่าไม่ได้ จะมีประโยชน์อะไร?

ตอบ — เพราะ ค่าส่วนใหญ่ในโปรแกรมไม่ควรเปลี่ยน การใช้ const ป้องกันไม่ให้เราเผลอเปลี่ยนค่าโดยไม่ตั้งใจ → bug น้อยลง อ่านโค้ดง่ายขึ้น (เห็น const ก็รู้ว่าค่านี้นิ่ง)

กฎทอง: ใช้ const เป็นค่าเริ่มต้นเสมอ — เปลี่ยนเป็น let เฉพาะตอนที่ต้องเปลี่ยนค่าจริง ๆ เท่านั้น

2.2 let — ใช้เมื่อค่าต้องเปลี่ยน

let = ตัวแปรที่เปลี่ยนค่าได้

javascript
let count = 0;
count = count + 1;     // ได้ — count กลายเป็น 1
count = 100;           // ได้ — count กลายเป็น 100
count++;               // ได้ — count กลายเป็น 101  (++ คือการเพิ่มค่าทีละ 1 — เรียนละเอียดบทที่ 03)

ใช้ let กับค่าที่ต้องเปลี่ยนระหว่างโปรแกรมทำงาน — เช่น ตัวนับ, ผลลัพธ์ที่สะสมไปเรื่อย ๆ, สถานะที่เปลี่ยน

2.3 ⚠️ const ไม่ได้ทำให้ object เปลี่ยนแปลงไม่ได้ทั้งหมด

นี่เป็นจุดที่มือใหม่เข้าใจผิดบ่อยมาก — ตั้งใจอ่านดี ๆ

const แค่ห้าม "เปลี่ยนกล่องไปชี้ของใหม่" — แต่ "ของข้างใน" ยังแก้ได้:

javascript
const user = { name: "Alice" };

user.name = "Bob";        // ✅ ได้! — แก้ของข้างใน object
user.age = 25;            // ✅ ได้! — เพิ่มของข้างใน object
console.log(user);        // { name: "Bob", age: 25 }

user = { name: "Carol" }; // ❌ Error! — เปลี่ยนให้กล่องชี้ object ใหม่

ทำไมเป็นแบบนี้? — เพราะ const user หมายถึง "กล่อง user จะชี้ไปที่ object เดิมตลอด" — แต่ object เดิมนั้น แก้ข้างในได้

💡 เปรียบเทียบ: const เหมือน "ป้ายชื่อบ้านที่ถอดไม่ได้" คุณเปลี่ยนป้ายให้ชี้บ้านหลังอื่นไม่ได้ แต่คุณยังเข้าไปจัดเฟอร์นิเจอร์ในบ้านหลังนั้นได้

(ถ้าอยากให้ object เปลี่ยนแปลงไม่ได้จริง ๆ ใช้ Object.freeze() — เรียกครั้งเดียวแล้วทุก property "ถูกแช่แข็ง":

javascript
const config = Object.freeze({ host: "localhost", port: 8080 });
config.port = 3000;        // ❌ TypeError ใน strict mode / ESM
                           //    (ESM เป็น strict mode อัตโนมัติ — หนังสือเล่มนี้ใช้ ESM
                           //    เรียนละเอียดเรื่อง strict mode ในบทที่ 05)

หมายเหตุ: ถ้าโค้ดรันใน "sloppy mode" (CommonJS หรือ <script> แบบเก่าที่ไม่มี "use strict") การพยายามแก้จะ เงียบเฉย ๆ ไม่ throw — แต่ทั้งบทนี้และทั้งหนังสือเราอยู่บน ESM ซึ่ง strict โดยอัตโนมัติ. เรียนละเอียดบทที่ 06)

2.4 var — วิธีเก่า อย่าใช้

var คือวิธีประกาศตัวแปรแบบเก่า (ก่อน ES6) — มันมีพฤติกรรมแปลก ๆ ที่ทำให้เกิดบั๊ก

ปัญหาหลักของ var:

ปัญหา 1: ไม่เคารพ block (ขอบเขต { })

javascript
if (true) {
  var x = 10;
}
console.log(x);      // 10 — x หลุดออกมานอก if ได้!

if (true) {
  let y = 10;
}
console.log(y);      // ❌ Error — y อยู่แค่ใน if (ถูกต้อง)

let/const "อยู่ในกรอบ" ของ { } — แต่ var หลุดออกมาได้ ทำให้โค้ดคาดเดายาก

ปัญหา 2: ประกาศซ้ำได้โดยไม่ error

javascript
var a = 1;
var a = 2;           // ✅ ไม่ error — แต่อันตราย เผลอประกาศซ้ำไม่รู้ตัว

let b = 1;
let b = 2;           // ❌ Error — เตือนเรา (ดี)

กฎทอง: ปี 2026 อย่าใช้ var เด็ดขาด — ใช้ const กับ let เท่านั้น. เรียนรู้จัก var ไว้แค่เพื่ออ่านโค้ดเก่าออก

2.5 Block Scope — ขอบเขตของตัวแปร

{ } (ปีกกา) สร้าง "block" — let/const ที่ประกาศใน block จะอยู่แค่ใน block นั้น:

javascript
{
  const secret = "ความลับ";
  console.log(secret);     // "ความลับ" — ใช้ได้ (อยู่ใน block)
}
console.log(secret);       // ❌ ReferenceError: secret is not defined
                           //    (ออกนอก block แล้ว ตัวแปรหายไป)

นี่เป็นเรื่องดี — มันทำให้ตัวแปร "ไม่รั่ว" ออกไปที่อื่น โค้ดเป็นระเบียบ

2.6 Temporal Dead Zone (TDZ — เขตห้ามชั่วคราว) — ใช้ตัวแปรก่อนประกาศ

ลองดู:

javascript
console.log(name);        // ❌ ReferenceError: Cannot access 'name' before initialization
                          //    (ใช้ตัวแปรก่อนบรรทัดประกาศไม่ได้)
let name = "Alice";

JavaScript ไม่อนุญาตให้ใช้ตัวแปร let/const ก่อนบรรทัดที่ประกาศ — ช่วงตั้งแต่ต้น block (หรือขอบเขต function/module) จนถึงบรรทัดประกาศ เรียกว่า TDZ (ช่วงที่ตัวแปรมีอยู่ในระบบแล้ว แต่ยังใช้ไม่ได้)

นี่เป็นเรื่องดี — มันบังคับให้เรา "ประกาศตัวแปรก่อนใช้" ซึ่งทำให้โค้ดอ่านง่าย

(ตรงนี้เป็นอีกข้อที่ var แย่กว่า — var ใช้ก่อนประกาศได้แต่ได้ค่า undefined ซึ่งทำให้บั๊กหายาก)


Part 3: ชนิดข้อมูลของ JavaScript

ข้อมูลในโปรแกรมมีหลาย "ชนิด" — ตัวเลขก็แบบหนึ่ง ข้อความก็อีกแบบ. JavaScript แบ่งชนิดข้อมูลเป็น 2 กลุ่มใหญ่:

  1. Primitive types — ข้อมูล "พื้นฐาน" (เลข, ข้อความ, true/false, ฯลฯ)
  2. Object types — ข้อมูลที่ "ซับซ้อน" (object, array, function)

ความต่างระหว่าง 2 กลุ่มนี้สำคัญมาก — เราจะคุยกันใน Part 6

3.1 เช็คชนิดข้อมูลด้วย typeof

JavaScript มีคำสั่ง typeof ไว้ถามว่า "ค่านี้เป็นชนิดอะไร":

javascript
typeof 42            // "number"
typeof "hello"       // "string"
typeof true          // "boolean"

เราจะใช้ typeof ตลอดบทนี้เพื่อพิสูจน์เรื่องต่าง ๆ


Part 4: Primitive Types — 7 ชนิด

JavaScript มี primitive type ทั้งหมด 7 ชนิด — มาดูทีละตัว

4.1 Number — ตัวเลข

ในภาษาอื่นมักแยกเลขจำนวนเต็ม (int) กับเลขทศนิยม (float) — แต่ JavaScript มีชนิดเลขแค่ชนิดเดียว คือ number ใช้ได้ทั้งหมด:

javascript
let age = 25;            // จำนวนเต็ม
let price = 99.99;       // ทศนิยม
let temp = -10;          // ติดลบ
let big = 1_000_000;     // ใส่ _ คั่นได้ เพื่ออ่านง่าย (= 1000000)

ค่าพิเศษของ number:

javascript
Infinity                 // อนันต์ (เช่น 1/0 — JS ไม่ throw error เหมือนภาษาอื่น)
-Infinity                // ลบอนันต์
NaN                      // "Not a Number" — ผลของการคำนวณที่ผิดพลาด

NaN เกิดเมื่อทำเลขที่ "ทำไม่ได้":

javascript
let result = "abc" * 2;
console.log(result);     // NaN — เอาข้อความมาคูณ ทำไม่ได้

⚠️ ปัญหาทศนิยม — เรื่องที่ต้องรู้

ลองพิมพ์ใน Console:

javascript
0.1 + 0.2

คุณคาดว่าได้ 0.3 ใช่ไหม? — แต่ JavaScript ตอบ 0.30000000000000004

นี่ไม่ใช่บั๊กของ JavaScript — มันเป็นข้อจำกัดของ "การเก็บทศนิยมในคอมพิวเตอร์" (ทุกภาษาที่ใช้ floating-point เป็นเหมือนกัน) — คอมพิวเตอร์เก็บเลขเป็นเลขฐาน 2 และ 0.1 ในฐาน 2 เป็นทศนิยมไม่รู้จบ เลยปัดเศษนิดหน่อย

วิธีรับมือ:

javascript
// ❌ อย่าเทียบทศนิยมตรง ๆ
0.1 + 0.2 === 0.3              // false

// ✅ ปัดก่อนเทียบ
Math.round((0.1 + 0.2) * 100) / 100 === 0.3      // true

// หรือใช้ toFixed สำหรับแสดงผล
(0.1 + 0.2).toFixed(2)        // "0.30"

→ ถ้าทำงานกับเงิน — เก็บเป็น "สตางค์" (จำนวนเต็ม) แทนการเก็บเป็น "บาท.สตางค์" (ทศนิยม) เพื่อเลี่ยงปัญหานี้

4.2 String — ข้อความ

ข้อความเขียนได้ 3 แบบ:

javascript
let a = "double quotes";      // เครื่องหมายคำพูดคู่
let b = 'single quotes';      // เครื่องหมายคำพูดเดี่ยว
let c = `backticks`;          // backtick — พิเศษ (อธิบายข้างล่าง)

"..." กับ '...' ใช้เหมือนกันทุกอย่าง — เลือกอันใดอันหนึ่งให้สม่ำเสมอ

Template Literal — backtick พิเศษยังไง

`...` (backtick) มี 2 ความสามารถพิเศษ:

1. แทรกตัวแปรลงในข้อความ ด้วย ${ }

javascript
let name = "Alice";
let age = 25;

// แบบเก่า — ต้อง + ต่อกันยุ่ง ๆ
let msg1 = "ชื่อ " + name + " อายุ " + age + " ปี";

// แบบใหม่ — ใช้ ${ } อ่านง่ายกว่ามาก
let msg2 = `ชื่อ ${name} อายุ ${age} ปี`;

ภายใน ${ } ใส่ expression อะไรก็ได้:

javascript
`ปีหน้าอายุ ${age + 1} ปี`            // "ปีหน้าอายุ 26 ปี"
`ชื่อตัวใหญ่: ${name.toUpperCase()}`   // "ชื่อตัวใหญ่: ALICE"

2. เขียนข้อความหลายบรรทัดได้

javascript
let poem = `บรรทัดที่หนึ่ง
บรรทัดที่สอง
บรรทัดที่สาม`;

กฎทอง: ใช้ template literal (backtick) เป็นค่าเริ่มต้นในงานสมัยใหม่ — โดยเฉพาะเวลาต้องแทรกตัวแปร

คำสั่งจัดการ String ที่ใช้บ่อย

javascript
let s = "Hello, World!";

s.length                  // 13 — จำนวนตัวอักษร
s.toUpperCase()           // "HELLO, WORLD!"
s.toLowerCase()           // "hello, world!"
s[0]                      // "H" — ตัวอักษรตำแหน่งที่ 0
s.includes("World")       // true — มีคำว่า "World" ไหม
s.startsWith("Hello")     // true
s.endsWith("!")           // true
s.indexOf("World")        // 7 — "World" อยู่ตำแหน่งที่เท่าไหร่
s.slice(0, 5)             // "Hello" — ตัดเอาตำแหน่ง 0 ถึง 5
s.replace("World", "JS")  // "Hello, JS!"
s.trim()                  // ตัดช่องว่างหน้า-หลังออก
s.split(", ")             // ["Hello", "World!"] — แยกเป็น array
s.at(-1)                  // "!" — ตัวอักษรจากท้าย (ES2022: -1 = ตัวสุดท้าย)
s.replaceAll("l", "L")    // "HeLLo, WorLd!" — แทนที่ทุกตัวที่พบ

⚠️ String เปลี่ยนแปลงไม่ได้

javascript
let s = "hello";
s[0] = "H";               // ❌ TypeError ใน strict mode / ESM
                          //    (Cannot assign to read only property '0' of string)
console.log(s);           // ถ้าโค้ดรันถึงตรงนี้ได้ (sloppy mode เก่า) — ยัง "hello"

s = s.toUpperCase();      // ✅ ต้อง assign กลับ
console.log(s);           // "HELLO"

💡 หนังสือเล่มนี้ใช้ ESM ("type": "module") ซึ่งเป็น strict mode อัตโนมัติ — การพยายามแก้ตัวอักษรใน string จะ throw error ทันที (V8/Node.js/Chrome ทำได้แน่นอน). ใน "sloppy mode" (CommonJS แบบเก่าหรือ <script> ที่ไม่มี "use strict") มันจะเงียบเฉย ๆ ทำให้ดีบักยาก — อีกเหตุผลที่แนะนำ ESM

คำสั่ง string ทุกตัว ไม่แก้ของเดิม — มัน "สร้างข้อความใหม่" แล้วคืนกลับมา ถ้าอยากเก็บผลต้อง assign

4.3 Boolean — จริง/เท็จ

มีแค่ 2 ค่า:

javascript
let isActive = true;
let isDeleted = false;

ใช้กับการตัดสินใจ (if, loop) — เรียนบทที่ 04

Truthy / Falsy — ค่าที่ "เหมือนจริง" และ "เหมือนเท็จ"

JavaScript มีแนวคิดสำคัญ — ทุกค่าสามารถถูกมองเป็น "จริง" หรือ "เท็จ" ได้ แม้ไม่ใช่ boolean

ค่าที่ถือว่า "เท็จ" (falsy) หลัก ๆ มี 8 ตัว:

javascript
false
0                    // เลขศูนย์
-0                   // ลบศูนย์ (มือใหม่ยังไม่ต้องสนใจ)
0n                   // BigInt ศูนย์ (เลขจำนวนเต็มใหญ่มาก ๆ — ดูหัวข้อ 4.7)
""                   // ข้อความว่าง
null
undefined
NaN

💡 มือใหม่โฟกัส 6 ตัวหลักก่อน: false, 0, "", null, undefined, NaN — 6 ตัวนี้เจอบ่อยในงานจริง ส่วน -0 กับ 0n เป็นกรณีพิเศษที่แทบไม่ได้ใช้ตอนเริ่มต้น (เก็บไว้รู้ว่า "มี" ก็พอ ยังไม่ต้องจำ)

📝 เกร็ดสำหรับคนรู้เยอะ: จริง ๆ มีค่าที่ 9 ที่ falsy คือ document.all (object เก่าของเบราว์เซอร์ยุค Internet Explorer หรือ IE — มีเฉพาะใน browser ไม่มีใน Node.js) — เป็น quirk (พฤติกรรมแปลกที่หลงเหลือ) ที่ spec (ข้อกำหนดภาษา) อนุญาตไว้เพื่อ backward-compat (ให้เข้ากันได้กับโค้ดเก่า) ไม่ต้องสนใจ — เห็นแค่ตัวอย่างว่ากฎมีข้อยกเว้นแปลก ๆ บ้าง

ทุกค่าอื่นถือว่า "จริง" (truthy) — รวมถึงค่าที่อาจดูเหมือนเท็จ:

javascript
"0"                  // truthy! (เป็น string ที่ไม่ว่าง)
" "                  // truthy! (มี space ก็ไม่ว่าง)
[]                   // truthy! (array ว่างก็ truthy)
{}                   // truthy! (object ว่างก็ truthy)

ใช้แปลงเป็น boolean ด้วย Boolean():

javascript
Boolean("hello")     // true
Boolean("")          // false
Boolean(0)           // false
Boolean(42)          // true

จำ 6 falsy values หลัก ให้แม่น (false, 0, "", null, undefined, NaN) — มันสำคัญมากเวลาเขียน if (บทที่ 03-04) ส่วน -0 กับ 0n รู้ว่ามีก็พอ ยังไม่ต้องจำ

4.4 undefined — "ยังไม่มีค่า"

undefined แปลว่า "ตัวแปรนี้มีอยู่ แต่ยังไม่ได้ใส่ค่า":

javascript
let x;
console.log(x);           // undefined — ประกาศแล้วแต่ไม่ได้ใส่ค่า

JavaScript ใส่ undefined ให้อัตโนมัติเมื่อ:

  • ประกาศตัวแปรแต่ไม่ assign ค่า
  • เรียก property ของ object ที่ไม่มี
  • function ที่ไม่ return อะไร

4.5 null — "ตั้งใจให้ว่าง"

null แปลว่า "ตั้งใจให้ค่านี้ว่างเปล่า" — เราเป็นคนใส่เอง:

javascript
let selectedUser = null;     // ยังไม่มีใครถูกเลือก (เราตั้งใจ)

undefined ต่าง null ยังไง

ทั้งคู่หมายถึง "ไม่มีค่า" — แต่ความหมายต่างกัน:

  • undefined = "JavaScript ยังไม่ได้ใส่ค่า" (เกิดอัตโนมัติ)
  • null = "เราตั้งใจใส่ว่างไว้" (developer ใส่เอง)
javascript
typeof undefined         // "undefined"
typeof null              // "object" — ⚠️ พฤติกรรมเพี้ยนตั้งแต่ JS รุ่นแรก ๆ
                         //    เคยพยายามแก้แต่จะทำให้โค้ดเก่าพังเยอะ เลยถูกบรรจุเป็นส่วนหนึ่งของ spec ECMAScript ไปแล้ว

(อย่าตกใจที่ typeof null เป็น "object" — มันเป็นเรื่องในตำนานที่ทุกคนรู้กัน เป็นข้อยกเว้นที่ต้องจำว่า "เช็ค null ด้วย === null เท่านั้น อย่าพึ่ง typeof")

4.6 Symbol — ค่าที่ไม่ซ้ำใคร

Symbol สร้างค่าที่ "ไม่มีวันซ้ำ" — ใช้ในงานขั้นสูง:

javascript
const id1 = Symbol("id");
const id2 = Symbol("id");
id1 === id2              // false — แม้ชื่อเหมือนกัน ก็ไม่เท่ากัน

ตัวอย่างที่ใช้ Symbol จริง — ทำ "key ลับ" ใน object ไม่ให้ชนกับ key อื่น:

javascript
const HIDDEN = Symbol("hidden");
const user = { name: "Alice", [HIDDEN]: "secret-token" };
// คนอื่นเอาคีย์ "hidden" มาแทนของเราไม่ได้ เพราะ Symbol ไม่ซ้ำ

ใช้จริงน้อยสำหรับมือใหม่ — ส่วนใหญ่เจอตอนใช้ library ขั้นสูง (เช่น Symbol.iterator ทำของให้ for...of ใช้ได้). เรียนเพิ่มบทที่ 06

4.7 BigInt — เลขใหญ่มาก ๆ

ชนิด number (Number type) เก็บเลขจำนวนเต็มได้ แม่นยำ ใหญ่สุดประมาณ 9 พันล้านล้าน (9 × 10^15 หรือ Number.MAX_SAFE_INTEGER = 9,007,199,254,740,991) — เกินตัวเลขนี้ number ยังเก็บได้อยู่ แต่บางค่าจำนวนเต็มจะเริ่มไม่แม่นยำ (สูญเสีย precision กลายเป็นเลขปัดผิด) ไม่ใช่ error ที่ทำให้โปรแกรมพัง. ถ้าต้องการเลขจำนวนเต็มใหญ่กว่านั้นแบบไม่จำกัดและแม่นยำเป๊ะ ใช้ BigInt (เก็บได้ใหญ่เท่าที่ RAM ไหว):

javascript
const huge = 9007199254740993n;      // ใส่ n ต่อท้าย
const huge2 = BigInt("123456789012345678901234567890");

ใช้น้อยสำหรับมือใหม่ — รู้จักไว้ว่ามี

⚠️ BigInt กับ number ผสมกันในการคำนวณไม่ได้โดยตรง: 1n + 1 → TypeError — ต้องแปลงก่อน: 1n + BigInt(1) หรือ Number(1n) + 1


Part 5: Object Types — ข้อมูลที่ซับซ้อน

ทุกอย่างที่ไม่ใช่ primitive ทั้ง 7 ชนิด = object (รวม Array, Function, Date, Map, Set, RegExp, ฯลฯ — ทั้งหมดเป็น object พิเศษที่ขยายจาก object พื้นฐาน)

javascript
typeof {}                // "object"
typeof []                // "object" — array ก็เป็น object ชนิดหนึ่ง
typeof function(){}      // "function" — function เป็น object พิเศษ
typeof new Date()        // "object"

5.1 Object — เก็บข้อมูลเป็นคู่ key-value

Object คือวิธีจับกลุ่มข้อมูลที่เกี่ยวข้องกันไว้เป็นก้อนเดียว ในรูปแบบ "คู่ key-value" (เช่น คน 1 คนมี name, age, isActive) — เข้าถึงค่าด้วยจุด (user.name) หรือวงเล็บเหลี่ยม (user["age"]):

javascript
const user = {
  name: "Alice",          // key "name" → value "Alice"
  age: 25,
  isActive: true,
};

user.name                 // "Alice" — เข้าถึงด้วย . (จุด)
user["age"]               // 25 — เข้าถึงด้วย [ ]

(เรียนละเอียดบทที่ 06)

5.2 Array — เก็บข้อมูลเป็นรายการ

javascript
const fruits = ["แอปเปิล", "กล้วย", "ส้ม"];

fruits[0]                 // "แอปเปิล" — ตัวแรก (เริ่มนับจาก 0)
fruits.length             // 3

(เรียนละเอียดบทที่ 07)


Part 6: Primitive ต่าง Object ยังไง — เรื่องสำคัญที่สุดของบทนี้

นี่คือแนวคิดที่ทำให้มือใหม่งงและเขียนบั๊กบ่อยที่สุด — ตั้งใจอ่านมาก ๆ

6.1 Primitive — เก็บแบบ "ค่า" (copy by value — ก๊อปค่าออกมาเป็นอิสระ)

เมื่อ assign primitive จากตัวแปรหนึ่งไปอีกตัว — มัน copy ค่า:

javascript
let a = 10;
let b = a;        // b ได้ "สำเนาของค่า" 10

b = 20;           // เปลี่ยน b
console.log(a);   // 10 — a ไม่เปลี่ยน (เป็นคนละค่ากันแล้ว)
console.log(b);   // 20

a กับ b เป็นอิสระต่อกัน — เปลี่ยนตัวหนึ่งไม่กระทบอีกตัว

6.2 Object — เก็บแบบ "การอ้างอิง" (copy by reference — ก๊อปที่อยู่อ้างอิง สองตัวแปรชี้ของชิ้นเดียวกัน)

เมื่อ assign object — มัน copy "ที่อยู่" (reference) ไม่ใช่ copy ตัว object:

javascript
let user1 = { name: "Alice" };
let user2 = user1;          // user2 ได้ "ที่อยู่เดียวกัน" กับ user1

user2.name = "Bob";          // เปลี่ยนผ่าน user2
console.log(user1.name);     // "Bob" — user1 เปลี่ยนตาม!

ทำไม user1 เปลี่ยนตาม? — เพราะ user1 กับ user2 ชี้ไปที่ object ตัวเดียวกัน

ดูภาพ:

💡 เปรียบเทียบ: primitive เหมือน "ถ่ายเอกสาร" — ได้สำเนาแยกกัน. object เหมือน "ให้ที่อยู่บ้าน" — 2 คนได้ที่อยู่เดียวกัน ใครเข้าไปจัดบ้านก็เห็นเหมือนกัน

6.3 ผลที่ตามมา — เปรียบเทียบ object

javascript
{} === {}                          // false — คนละ object (คนละที่อยู่)
[] === []                          // false

let x = { v: 1 };
let y = x;
x === y                            // true — ที่อยู่เดียวกัน

object 2 ตัวที่ "หน้าตาเหมือนกัน" ไม่ได้ถือว่าเท่ากัน — JavaScript เทียบที่ "ที่อยู่" ไม่ใช่ "เนื้อหา"

6.4 การ copy object — shallow (ตื้น) vs deep (ลึก)

ถ้าอยากได้ object ใหม่จริง ๆ (ไม่ใช่ที่อยู่เดิม):

javascript
const original = { name: "Alice", age: 25 };

// copy แบบตื้น (shallow copy)
const copy1 = { ...original };          // ใช้ ... (spread — กระจาย property ของ object เดิมเข้า object ใหม่ เรียนละเอียดบทที่ 06)
const copy2 = Object.assign({}, original);

copy1.name = "Bob";
console.log(original.name);             // "Alice" — ไม่กระทบ ✅

แต่ shallow copy มีข้อจำกัด — ถ้า object มี object ซ้อนข้างใน:

javascript
const original = { name: "Alice", address: { city: "Bangkok" } };
const copy = { ...original };

copy.address.city = "Tokyo";
console.log(original.address.city);     // "Tokyo" — ⚠️ กระทบ! เพราะ address ข้างในยัง share กัน

ถ้าต้องการ copy ลึกทุกชั้น (deep copy) — ใช้ structuredClone() (built-in มาตั้งแต่ Node.js 17+ และเบราว์เซอร์สมัยใหม่ทุกตัว ตั้งแต่ปี 2022):

javascript
const deepCopy = structuredClone(original);
deepCopy.address.city = "Tokyo";
console.log(original.address.city);     // "Bangkok" — ไม่กระทบ ✅

→ จำ: { ...obj } = copy ตื้น (พอใช้ส่วนใหญ่), structuredClone(obj) = copy ลึกทั้งหมด

⚠️ ข้อจำกัดของ structuredClone: ไม่รองรับ function (ไม่ถูก clone), DOM node, WeakMap/WeakSet — สำหรับ object ทั่วไป (plain object, array, Date, Map, Set) ใช้ได้ปกติ


Part 7: การแปลงชนิดข้อมูล

บ่อยครั้งเราต้องแปลงชนิดข้อมูล — เช่นรับ input จากผู้ใช้ (เป็น string) แล้วอยากเอามาคำนวณ (เป็น number)

7.1 แปลงเป็น Number

javascript
Number("42")             // 42
Number("3.14")           // 3.14
Number("  42  ")         // 42 — ตัดช่องว่างให้
Number("abc")            // NaN — แปลงไม่ได้
Number("")               // 0 — ⚠️ ข้อความว่างกลายเป็น 0 (น่าแปลกใจ)
Number(true)             // 1
Number(false)            // 0
Number(null)             // 0
Number(undefined)        // NaN

// วิธีลัด — ใส่ + ข้างหน้า
+"42"                    // 42

// parseInt / parseFloat — ยืดหยุ่นกว่า (ดึงเลขจากข้อความที่ปน)
parseInt("42px")         // 42 — ดึงเฉพาะเลข
parseFloat("3.14 บาท")   // 3.14

⚠️ ระวัง Number("") ได้ 0 — เวลา validate ฟอร์ม ถ้าผู้ใช้ไม่กรอกอะไร แล้วเราแปลงเป็นเลข จะกลายเป็น 0 โดยไม่ตั้งใจ

7.2 แปลงเป็น String

แปลงค่าชนิดอื่นให้เป็นข้อความด้วย String() หรือวิธีลัด (.toString(), template literal, ต่อกับ string ว่าง) — ใช้บ่อยเวลาแสดงผลหรือประกอบข้อความ:

javascript
String(42)               // "42"
String(true)             // "true"
String(null)             // "null"
String([1, 2, 3])        // "1,2,3"

// วิธีลัด
(42).toString()          // "42"
`${42}`                  // "42" — template literal
"" + 42                  // "42" — ต่อกับ string ว่าง

7.3 แปลงเป็น Boolean

แปลงค่าเป็น true/false ด้วย Boolean() หรือวิธีลัด !! (อัศเจรีย์คู่) โดยยึดกฎ truthy/falsy จาก Part 4.3 — ใช้บ่อยเวลาเช็คเงื่อนไข:

ใช้กฎ truthy/falsy จาก Part 4.3:

javascript
Boolean("hello")         // true
Boolean("")              // false
Boolean(0)               // false
Boolean(42)              // true

// วิธีลัด — ใช้ !! (อัศเจรีย์คู่)
!!"hello"                // true
!!0                      // false

7.4 การแปลงอัตโนมัติ (Implicit Coercion) — ต้องระวัง

จากบทที่ 00 — JavaScript แปลงชนิดให้อัตโนมัติบางครั้ง ทำให้เกิดผลแปลก ๆ:

javascript
"5" + 3                  // "53"  — + เห็น string เลยต่อข้อความ
"5" - 3                  // 2     — - ใช้กับเลขเท่านั้น เลยแปลง "5" เป็น 5
"5" * "2"                // 10
true + 1                 // 2     — true แปลงเป็น 1
[] + []                  // ""    — array ว่างแปลงเป็น string ว่าง

💡 เกร็ด: [] + {} ได้ "[object Object]" แต่ {} + [] ได้ 0 — ผลต่างกันเพราะ {} ในตำแหน่งเริ่มต้น statement ถูก parser มองเป็น block ว่าง ไม่ใช่ object literal (สับสนมาก — นี่เป็นเหตุผลหนึ่งที่ต้องแปลงชนิดเองเสมอ)

วิธีรับมือ: แปลงชนิดเองให้ชัดเจนเสมอ — อย่าพึ่งการแปลงอัตโนมัติ

javascript
// ❌ พึ่งการแปลงอัตโนมัติ — เสี่ยง
let total = userInput + 10;

// ✅ แปลงเองให้ชัด
let total = Number(userInput) + 10;

Part 8: ตั้งชื่อตัวแปร

8.1 กฎ

JavaScript มีกฎตายตัวว่าตั้งชื่อตัวแปรอย่างไรได้บ้าง — ห้ามขึ้นต้นด้วยตัวเลข, ห้ามมีขีดกลาง, ห้ามใช้คำสงวน (เช่น let) แต่ขึ้นต้นด้วย _ หรือ $ ได้ มาดูตัวอย่างที่ได้/ไม่ได้:

javascript
let userName;        // ✅ ได้
let _private;        // ✅ ได้ — ขึ้นต้นด้วย _
let $element;        // ✅ ได้ — ขึ้นต้นด้วย $
let user2;           // ✅ ได้ — มีตัวเลข (แต่ห้ามขึ้นต้นด้วยเลข)

let 2user;           // ❌ ขึ้นต้นด้วยเลขไม่ได้
let user-name;       // ❌ มีขีดกลางไม่ได้
let let;             // ❌ ใช้คำสงวนไม่ได้

8.2 ธรรมเนียมการตั้งชื่อ

นอกจากกฎที่ภาษาบังคับ ยังมี "ธรรมเนียม" ที่นักพัฒนายึดร่วมกันเพื่อให้โค้ดอ่านง่าย:

  • camelCase ("เนินอูฐ" — ตัวแรกเล็ก คำต่อไปขึ้นต้นด้วยตัวใหญ่ ดูเหมือนหลังอูฐ เช่น firstName) สำหรับตัวแปรและฟังก์ชัน
  • PascalCase (ทุกคำขึ้นต้นด้วยตัวใหญ่ เช่น UserAccount) สำหรับ class
  • UPPER_SNAKE_CASE (ตัวใหญ่ทั้งหมด คั่นด้วย _ เช่น MAX_RETRIES) สำหรับค่าคงที่สำคัญ
javascript
// camelCase — สำหรับตัวแปรและ function (ตัวแรกเล็ก คำต่อไปตัวใหญ่)
let firstName;
let isActive;
let totalPrice;

// PascalCase — สำหรับ class (ทุกคำขึ้นต้นด้วยตัวใหญ่)
class UserAccount {}

// UPPER_SNAKE_CASE — สำหรับค่าคงที่สำคัญ
const MAX_RETRIES = 3;
const API_URL = "https://...";

8.3 ตั้งชื่อให้สื่อความหมาย

javascript
// ❌ ชื่อไม่สื่อ
let x = 86400;
let d = users.filter(u => u.a);

// ✅ ชื่อสื่อความหมาย
let secondsPerDay = 86400;
let activeUsers = users.filter(user => user.isActive);

ชื่อตัวแปรที่ดี = โค้ดที่อ่านเข้าใจได้โดยไม่ต้องมี comment


Part 9: Lab — ลงมือทำ

Lab 1: ทายผลลัพธ์

เปิด Console แล้วทายก่อนพิมพ์ว่าได้อะไร:

javascript
typeof 42
typeof "hello"
typeof true
typeof undefined
typeof null
typeof {}
typeof []
"5" + 3
"5" - 3
0.1 + 0.2 === 0.3
Number("")
Boolean("0")
Boolean([])

Lab 2: พิสูจน์ primitive vs object

ลองรันโค้ดนี้เพื่อเห็นกับตาว่า primitive (เช่น number) ก๊อปค่าแยกกันอิสระ ส่วน object จะแชร์การอ้างอิงเดียวกัน (แก้ตัวหนึ่งกระทบอีกตัว) จุดนี้คือจุดที่มือใหม่สับสนบ่อยที่สุด:

javascript
// primitive — อิสระต่อกัน
let a = 5;
let b = a;
b = 99;
console.log("a =", a, "/ b =", b);     // a = 5 / b = 99

// object — share กัน
let obj1 = { count: 0 };
let obj2 = obj1;
obj2.count = 99;
console.log("obj1.count =", obj1.count);   // 99 — กระทบ!

// แก้ด้วย copy
let obj3 = { count: 0 };
let obj4 = { ...obj3 };   // { ...obj3 } คือ spread operator — กระจาย property ของ obj3 ออกมา (เรียนละเอียดบทที่ 03)
obj4.count = 99;
console.log("obj3.count =", obj3.count);   // 0 — ไม่กระทบ

Lab 3: เครื่องคิดเลขส่วนลด

javascript
const price = 1200;
const discountPercent = 15;

const discountAmount = price * (discountPercent / 100);
const finalPrice = price - discountAmount;

console.log(`ราคาเต็ม: ${price} บาท`);
console.log(`ส่วนลด: ${discountPercent}% = ${discountAmount} บาท`);
console.log(`ราคาสุทธิ: ${finalPrice} บาท`);

Part 10: Checkpoint

  1. const ต่าง let ยังไง? ควรใช้อันไหนเป็นค่าเริ่มต้น?
  2. ทำไม const user = {...} แล้ว user.name = "Bob" ถึงทำได้?
  3. ทำไมไม่ควรใช้ var?
  4. Block scope คืออะไร? Temporal Dead Zone คืออะไร?
  5. Primitive type มีกี่ชนิด? อะไรบ้าง?
  6. 0.1 + 0.2 === 0.3 ได้ผลอะไร? ทำไม?
  7. Falsy values หลัก 6 ตัวคืออะไร? (แล้วมีอีก 2 ตัวพิเศษที่ไม่ต้องจำตอนเริ่มต้น)
  8. undefined ต่าง null ยังไง?
  9. Primitive copy by value / Object copy by reference — ต่างกันยังไง?
  10. { ...obj } ต่าง structuredClone(obj) ยังไง?

Part 11: สรุปบทนี้

  • ตัวแปร = กล่องเก็บข้อมูลที่มีชื่อ
  • const เป็นค่าเริ่มต้น — เปลี่ยนเป็น let เมื่อต้องเปลี่ยนค่า — ไม่ใช้ var
  • const กับ object — เปลี่ยน "ที่ชี้" ไม่ได้ แต่แก้ข้างใน object ได้
  • 7 primitive types: number, string, boolean, undefined, null, symbol, bigint
  • ทุกอย่างที่เหลือ = object (รวม array, function)
  • number มีปัญหาทศนิยม (0.1 + 0.2 !== 0.3) — ระวัง
  • Falsy หลัก 8 ตัว: false, 0, -0, 0n, "", null, undefined, NaN — ที่เหลือ truthy (มี document.all เป็นข้อยกเว้นแปลก ๆ ตัวที่ 9)
  • Primitive copy by value (อิสระ) — Object copy by reference (share ที่อยู่)
  • แปลงชนิดเองให้ชัดเจน — อย่าพึ่งการแปลงอัตโนมัติ

บทต่อไป — Operators: เครื่องหมายต่าง ๆ ที่ใช้คำนวณและเปรียบเทียบ


← บทที่ 01 | สารบัญ | บทที่ 03: Operators →