โหมดมืด
บทที่ 06 — Objects, Prototypes และ Class
Object คือวิธีที่ JavaScript ใช้จัดกลุ่มข้อมูลที่เกี่ยวข้องกัน — เป็นโครงสร้างข้อมูลที่ใช้บ่อยที่สุดในภาษานี้
อ่านจบบทนี้คุณจะ:
- สร้างและจัดการ object ได้คล่อง
- เข้าใจ "prototype" — กลไกเบื้องหลังที่ทำให้ object ทำงาน
- เขียน
classเป็น พร้อมเข้าใจว่ามันเป็นแค่ "เปลือก" ของ prototype - รู้จัก
MapและSet— โครงสร้างข้อมูลที่บางทีดีกว่า object/array
ใช้เวลา 3 ชั่วโมง
Part 1: Object พื้นฐาน
1.1 Object คืออะไร
Object = กลุ่มของข้อมูลที่เก็บเป็นคู่ "key: value"
ลองนึกถึง "บัตรประชาชน" — มันรวมข้อมูลของคนหนึ่งคน: ชื่อ, อายุ, ที่อยู่ — object ก็ทำแบบเดียวกัน
javascript
const user = {
name: "Alice", // key "name" → value "Alice"
age: 25, // key "age" → value 25
isActive: true,
};แต่ละบรรทัดเรียกว่า "คุณสมบัติ" (property) — name คือ key, "Alice" คือ value
1.2 เข้าถึงข้อมูลใน Object
มี 2 วิธี:
วิธี dot (.) — ใช้บ่อยสุด:
javascript
user.name // "Alice"
user.age // 25วิธี bracket ([ ]) — ใช้เมื่อ key มีอักขระพิเศษ หรือ key เป็นตัวแปร:
javascript
user["name"] // "Alice"
const key = "age";
user[key] // 25 — key เป็นตัวแปร ต้องใช้ bracket
const obj = { "my key": 1 };
obj["my key"] // 1 — key มีช่องว่าง ต้องใช้ bracketจำง่าย: รู้ชื่อ key แน่นอน → . / key เป็นตัวแปร หรือมีอักขระพิเศษ → [ ]
1.3 เพิ่ม / แก้ / ลบ property
object ใน JS เปลี่ยนแปลงได้ตลอด — เพิ่ม property ใหม่ด้วยการกำหนดค่า, แก้ค่าเดิมด้วยการกำหนดทับ, และลบด้วย delete (สังเกตว่าแม้ประกาศด้วย const ก็ยังแก้ property ภายในได้ เพราะ const ล็อกแค่การอ้างอิง):
📝 หมายเหตุ:
deleteเป็น operator พิเศษของภาษา ไม่ใช่ function — ใช้ลบ property ของ object เท่านั้น (ไม่ใช้ลบตัวแปร)
javascript
const user = { name: "Alice" };
user.age = 25; // เพิ่ม property ใหม่
user.name = "Bob"; // แก้ค่าเดิม
delete user.age; // ลบ property (delete เป็น operator)
console.log("name" in user); // true — เช็คว่ามี key นี้ไหม1.4 Method — function ใน object
property ที่เป็น function เรียกว่า "method":
javascript
const user = {
name: "Alice",
greet() { // method
return "สวัสดี " + this.name; // this = object ตัวเอง (จำบทที่ 05)
},
};
user.greet(); // "สวัสดี Alice"(จำเรื่อง this จากบทที่ 05 — ใน method ปกติ this คือ object ที่เรียก)
1.5 Property Shorthand — เขียนสั้น
เวลาสร้าง object จากตัวแปรที่ชื่อตรงกับ key อยู่แล้ว JavaScript มีรูปย่อให้เขียนแค่ชื่อครั้งเดียว ({ name } แทน { name: name }) — ลดความซ้ำซ้อน เจอบ่อยมากในโค้ดจริง:
ถ้าชื่อตัวแปรตรงกับชื่อ key:
javascript
const name = "Alice";
const age = 25;
// แบบยาว
const user1 = { name: name, age: age };
// แบบสั้น (shorthand) — เหมือนกัน
const user2 = { name, age };1.6 Computed Property — key เป็นตัวแปร
ใช้ [ ] ตอนสร้าง object เพื่อให้ key มาจากตัวแปร:
javascript
const key = "status";
const value = "active";
const obj = {
[key]: value, // key มาจากตัวแปร
};
console.log(obj); // { status: "active" }Part 2: วน Object
จากบทที่ 04 เราเคยเจอ for...in ที่วน key ของ object ได้ แต่ for...in วนรวม property ที่มาจาก prototype ด้วย (ซึ่งมักไม่ใช่สิ่งที่เราอยากได้) ในงานจริงจึงแนะนำให้ใช้ Object.keys / Object.values / Object.entries แทน เพราะสามคำสั่งนี้คืนแค่ property ที่ object สร้างขึ้นเองเท่านั้น ไม่รวมของที่สืบทอดมาจาก prototype
📝 กฎง่าย ๆ สำหรับตอนนี้: ใช้
Object.keys/Object.values/Object.entriesแทนfor...inเสมอเมื่อวน object — ปลอดภัยกว่าเสมอไม่ว่า object จะซับซ้อนแค่ไหน ไม่ต้องกังวลเรื่อง prototype chain (เดี๋ยวเราจะอธิบายเรื่องนี้ใน Part 4)
2.1 Object.keys / values / entries
javascript
const user = { name: "Alice", age: 25, city: "Bangkok" };
Object.keys(user) // ["name", "age", "city"] — array ของ key
Object.values(user) // ["Alice", 25, "Bangkok"] — array ของ value
Object.entries(user) // [["name","Alice"], ["age",25], ["city","Bangkok"]]เมื่อแปลงเป็น array แล้ว — วนด้วย for...of ได้ (ดีกว่า for...in):
javascript
for (const key of Object.keys(user)) {
console.log(key, "=", user[key]);
}
// หรือแกะ key+value พร้อมกันด้วย destructuring
for (const [key, value] of Object.entries(user)) {
console.log(key, "=", value);
}2.2 Object.fromEntries — ตรงข้ามกับ entries
javascript
const entries = [["name", "Alice"], ["age", 25]];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: "Alice", age: 25 }มีประโยชน์ตอน "แปลง object → array → ทำอะไรบางอย่าง → แปลงกลับ":
javascript
const prices = { apple: 30, banana: 15, orange: 25 };
// เพิ่มราคาทุกตัว 10%
const newPrices = Object.fromEntries(
Object.entries(prices).map(([name, price]) => [name, price * 1.1])
);📝 ตัวอย่างนี้ใช้
.map()ของ array — ถ้ายังไม่คุ้น ดูบทที่ 07 ที่เราอธิบายmap/filter/reduceละเอียด ตรงนี้เห็นภาพคร่าว ๆ ว่า "แปลงทุกตัว" ก็พอ
Part 3: Copy Object (ทบทวน + เพิ่ม)
จากบทที่ 02 — object copy แบบ reference. ทบทวนวิธี copy:
javascript
const original = { name: "Alice", age: 25 };
// shallow copy (copy ตื้น — ชั้นเดียว)
const copy1 = { ...original };
const copy2 = Object.assign({}, original);
// deep copy (copy ลึกทุกชั้น)
const copy3 = structuredClone(original);3.1 Object.freeze — ทำให้ object เปลี่ยนแปลงไม่ได้
จากบทที่ 02 — const ไม่ได้ห้ามแก้ข้างใน object. ถ้าอยากห้ามแก้จริง ๆ ใช้ Object.freeze:
javascript
const config = Object.freeze({
apiUrl: "https://api.example.com",
timeout: 5000,
});
config.timeout = 1000; // ❌ TypeError ใน strict mode / ES module
console.log(config.timeout); // 5000 — ไม่เปลี่ยน⚠️ โหมดสำคัญ: ใน strict mode หรือ ES module (ซึ่งเป็นค่า default ของโค้ดสมัยใหม่) การกำหนดค่าใหม่ (assign) ให้ property ของ object ที่ freeze แล้ว จะ throw TypeError ทันที — ไม่ใช่เงียบเหมือนสมัยก่อน (sloppy mode เก่าเท่านั้นที่เงียบ) ดังนั้นถ้าอยู่ในไฟล์
.mjs, ใน<script type="module">, หรือมี"use strict"→ จะได้ error
ใช้กับค่าคงที่ที่ไม่อยากให้ใครเผลอแก้ — error ที่ throw ออกมาช่วยจับบั๊กได้เร็วขึ้น
Part 4: Prototype (ต้นแบบ) — กลไกเบื้องหลัง Object
🚀 โซนขั้นสูง — ข้ามได้ (แต่ดีถ้าอ่าน)
prototype = ต้นแบบ — object อีกตัวที่ object ของเราเชื่อมไปหา เพื่อยืม property/method มาใช้
พูดง่าย ๆ: prototype คือ "object แม่แบบ" ที่ object ลูกสามารถยืม method มาใช้ได้ — เหมือนเด็กที่ยืมทักษะจากพ่อแม่โดยที่ไม่ต้องก็อปทุกอย่างมาเก็บเอง
เรื่อง prototype (ต้นแบบ) เป็นกลไก "เบื้องหลัง" ที่มือใหม่ไม่ได้เขียนตรง ๆ — ในงานจริงเราใช้
class(Part 5) ซึ่งจัดการ prototype ให้แล้ว ถ้าอ่านแล้วงง ข้ามไป Part 5 ได้เลย แล้วค่อยกลับมาเข้าใจลึกทีหลัง ขอแค่รู้ว่า "class เบื้องหลังคือ prototype (ต้นแบบ)" ก็พอ
นี่คือส่วนที่ลึกที่สุดของบท — เป็นกลไกที่ทำให้ object ของ JavaScript ทำงาน
4.1 ปัญหา — method ที่ใช้ร่วมกัน
ลองคิด: ถ้าเรามี user 1000 คน แต่ละคนมี method greet เหมือนกัน:
javascript
const user1 = { name: "Alice", greet() { return "Hi " + this.name; } };
const user2 = { name: "Bob", greet() { return "Hi " + this.name; } };
// ... 1000 คนเก็บ greet 1000 ชุดที่เหมือนกันเป๊ะ — เปลือง memory
→ ต้องการ "ที่เก็บ method ส่วนกลาง" ที่ทุก object ใช้ร่วมกัน — นั่นคือ prototype
4.2 ห่วงโซ่ต้นแบบ (Prototype Chain)
ทุก object ใน JavaScript มี "ตัวเชื่อม" ลับ ๆ ที่ชี้ไปยัง object อีกตัว เรียกว่า prototype (ต้นแบบ)
เมื่อเราเรียก property/method ของ object — JavaScript หาแบบนี้:
- หาใน object ตัวเองก่อน — เจอ → ใช้เลย
- ไม่เจอ → ไปหาใน prototype ของมัน
- ยังไม่เจอ → ไปหาใน prototype ของ prototype
- ... ไปเรื่อย ๆ จนสุดทาง (เป็น
null)
ลำดับนี้เรียกว่า ห่วงโซ่ต้นแบบ (prototype chain)
javascript
const arr = [1, 2, 3];
arr.map(...)
// JavaScript หา .map ในตัว arr → ไม่เจอ
// → ไปหาใน Array.prototype → เจอ! → ใช้นี่คือเหตุผลที่ array ทุกตัวมี .map, .filter — เพราะ method เหล่านี้อยู่ใน Array.prototype ที่ array ทุกตัวเชื่อมไปถึง
💡 เปรียบเทียบ: prototype chain เหมือน "ถามต่อ ๆ กัน" — ถามตัวเอง ไม่รู้ → ถามพ่อแม่ ไม่รู้ → ถามปู่ย่า — จนกว่าจะเจอคำตอบ หรือจนไม่มีใครให้ถามต่อแล้ว
4.3 ดู prototype
javascript
const obj = {};
Object.getPrototypeOf(obj) === Object.prototype; // true
const arr = [];
Object.getPrototypeOf(arr) === Array.prototype; // trueตอนนี้แค่ "เข้าใจว่ามันมี" ก็พอ — ในงานจริงเราไม่ค่อยยุ่งกับ prototype ตรง ๆ เพราะเราใช้ class แทน (ซึ่งจัดการ prototype ให้เรา)
Part 5: Class — เปลือกที่สวยงามของ Prototype
📌 สรุปสั้น ๆ ของ prototype (เผื่อยังไม่ชัวร์จาก Part 4): prototype = "ต้นแบบ" — object อีกตัวที่ object ของเราเชื่อมไปหาเพื่อยืม method ใช้ร่วมกัน เวลาเขียน
classแล้วประกาศ method ใน class — ภายในแล้ว JavaScript เก็บ method นั้นไว้ที่ prototype เพื่อให้ทุก instance ใช้ร่วมกัน (ประหยัด memory)
class เป็น syntax ที่ ES6 เพิ่มเข้ามา — มันคือ "วิธีเขียนที่อ่านง่าย" ของระบบ prototype
💡 ทำไมต้องใช้ class? ลองนึกภาพต้องสร้าง user 100 คน — ถ้าเขียน object literal 100 ชุดด้วยมือ จะซ้ำซากและเสียเวลามาก ด้วย class เราเขียน "พิมพ์เขียว" ครั้งเดียว แล้วใช้
newสร้าง instance ได้กี่ตัวก็ได้ — code สั้นกว่า บำรุงรักษาง่ายกว่า และ method ทุก instance ใช้ร่วมกันจาก prototype (ประหยัด memory)
5.1 Class พื้นฐาน
javascript
class User {
constructor(name, age) { // constructor — ทำงานตอนสร้าง object
this.name = name;
this.age = age;
}
greet() { // method
return "สวัสดี " + this.name;
}
haveBirthday() {
this.age++;
}
}
// สร้าง object จาก class ด้วย new
const alice = new User("Alice", 25);
alice.greet(); // "สวัสดี Alice"
alice.haveBirthday();
console.log(alice.age); // 26อธิบาย:
class User— ประกาศ class ชื่อUser(เหมือน "พิมพ์เขียว")constructor— function พิเศษ ทำงานตอนnew— ใช้ตั้งค่าเริ่มต้นnew User(...)— สร้าง object จริง (เรียก "instance" = ตัวอย่างจริงที่สร้างจาก class) จากพิมพ์เขียว- method ที่เขียนใน class จะอยู่ใน prototype → ทุก instance ใช้ร่วมกัน (แก้ปัญหา Part 4.1)
💡 เปรียบเทียบ: class = "พิมพ์เขียวบ้าน" — instance (จาก
new) = "บ้านจริงที่สร้างจากพิมพ์เขียว" — พิมพ์เขียวเดียวสร้างบ้านได้หลายหลัง
5.2 Class Field — ประกาศ property ตรง ๆ
นอกจากกำหนด property ใน constructor เราประกาศ property พร้อมค่าเริ่มต้นไว้ในตัว class ตรง ๆ ได้เลย (class field) — อ่านง่ายและเหมาะกับค่าที่มี default ชัดเจน:
javascript
class Counter {
count = 0; // class field — ค่าเริ่มต้น
step = 1;
increment() {
this.count += this.step;
}
}
const c = new Counter();
c.increment();
console.log(c.count); // 15.3 Private Field — # (ข้อมูลส่วนตัว)
ใส่ # หน้าชื่อ → property/method นั้นเข้าถึงได้แค่ในตัว class:
javascript
class BankAccount {
#balance = 0; // private — ข้างนอกแตะไม่ได้
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const acc = new BankAccount();
acc.deposit(100);
console.log(acc.getBalance()); // 100
// console.log(acc.#balance); // ❌ บรรทัดนี้ comment เพราะถ้า uncomment จะทำให้ทั้งไฟล์รันไม่ได้เลย
// // (ภาษาห้าม syntax `obj.#field` นอก class body
// // ตั้งแต่ขั้นแปลงโค้ด ไม่ใช่ตอนรัน — สคริปต์ไม่ทันได้รันเลย)⚠️ บรรทัด
acc.#balanceถูก comment ไว้เพราะถ้าไม่ comment — ทั้งไฟล์จะ parse ไม่ผ่าน (SyntaxError) ตั้งแต่ก่อนรันบรรทัดอื่นใด ไม่ใช่ error ตอน runtime ทดลองโดย uncomment ดูได้ — engine จะปฏิเสธทั้งสคริปต์ตั้งแต่ขั้นโหลด
#balance ทำหน้าที่เดียวกับ closure ใน "createBankAccount" ของบทที่ 05 — แต่เขียนด้วย class
5.4 Static — property/method ของตัว class เอง
static ทำให้ property/method เป็นของ "class" ไม่ใช่ของ "instance":
javascript
class MathHelper {
static PI = 3.14159;
static square(n) {
return n * n;
}
}
MathHelper.PI; // 3.14159 — เรียกจาก class ตรง ๆ
MathHelper.square(5); // 25
const m = new MathHelper();
m.square(5); // ❌ Error — static ไม่ได้อยู่ใน instanceใช้ static กับสิ่งที่ "เกี่ยวกับ class แต่ไม่ผูกกับ instance ใดตัวหนึ่ง" เช่น ค่าคงที่, factory method (= static method ที่สร้างและคืน instance ออกมา — เช่น User.fromJSON(...))
5.5 Getter / Setter — property ที่มี logic
get/set ทำให้เราเขียน method แต่เรียกใช้เหมือน property:
javascript
class Temperature {
#celsius = 0;
get celsius() { // getter — เรียกเหมือน property
return this.#celsius;
}
set celsius(value) { // setter
if (value < -273.15) throw new Error("ต่ำกว่าศูนย์สัมบูรณ์");
this.#celsius = value;
}
get fahrenheit() { // getter ที่คำนวณ
return this.#celsius * 9/5 + 32;
}
}
const t = new Temperature();
t.celsius = 25; // เรียก setter (เหมือน assign property)
t.celsius; // 25 — เรียก getter
t.fahrenheit; // 77 — getter ที่คำนวณให้Part 6: Inheritance — การสืบทอด
class สามารถ "สืบทอด" จาก class อื่น — เอา property/method มาใช้ต่อ แล้วเพิ่ม/แก้ได้
6.1 extends และ super
javascript
class Animal {
constructor(name) {
this.name = name;
}
eat() {
return this.name + " กำลังกิน";
}
}
class Dog extends Animal { // Dog สืบทอดจาก Animal
constructor(name, breed) {
super(name); // เรียก constructor ของ Animal
this.breed = breed;
}
bark() { // method เพิ่มของ Dog เอง
return this.name + " เห่า: โฮ่ง!";
}
}
const d = new Dog("เร็กซ์", "ลาบราดอร์");
d.eat(); // "เร็กซ์ กำลังกิน" — สืบทอดมาจาก Animal
d.bark(); // "เร็กซ์ เห่า: โฮ่ง!" — ของ Dog เองอธิบาย:
class Dog extends Animal— Dog สืบทอดทุกอย่างจาก Animalsuper(name)— เรียก constructor ของ class แม่ (Animal) — ต้องเรียกก่อนใช้this
6.2 เขียนทับ (Override) — เขียนทับ method ของแม่
class ลูกสามารถ "เขียนทับ" (override) method ที่สืบทอดมาจากแม่ เพื่อเปลี่ยนพฤติกรรมให้เหมาะกับตัวเอง — และยังเรียก method เดิมของแม่ได้ด้วย super.methodName():
javascript
class Cat extends Animal {
eat() { // เขียนทับ eat ของ Animal
return this.name + " กินอย่างสง่างาม";
}
}
const c = new Cat("มีโอ");
c.eat(); // "มีโอ กินอย่างสง่างาม" — ใช้ของ Cat ไม่ใช่ของ Animalเรียก method เดิมของแม่ด้วย super:
javascript
class Cat extends Animal {
eat() {
return super.eat() + " (แบบแมว)"; // เรียก eat ของ Animal แล้วเพิ่ม
}
}6.3 ⚠️ Composition over Inheritance (การประกอบดีกว่าการสืบทอด)
📌 คำแปลย่อ: composition over inheritance = "การประกอบดีกว่าการสืบทอด" — ใช้การจับกลุ่มความสามารถมาประกอบกัน แทนการสืบทอดเป็นชั้น ๆ
inheritance ดูทรงพลัง — แต่มันมีกับดัก: ถ้าสืบทอดลึกหลายชั้น (A → B → C → D) โค้ดจะซับซ้อน เปราะ แก้ยาก
คำแนะนำสมัยใหม่: "composition over inheritance" — แทนที่จะสืบทอดเป็นชั้น ๆ เราแค่ "ประกอบ" ความสามารถเข้าด้วยกัน เหมือนต่อ Lego — เลือกหยิบแต่ละชิ้นที่ต้องการมาประกอบ แทนที่จะสร้างสายการสืบทอดที่ยาว
⚠️ ข้อควรระวัง: pattern นี้เหมาะกับ method ที่ไม่มี state ส่วนตัว — ถ้าต้องการ state แยกต่อหัวหรือ override เฉพาะ instance พิจารณาใช้ class แทน
javascript
// แทนที่จะ class Duck extends Bird extends Animal...
// ประกอบความสามารถเข้าด้วยกัน
const canFly = {
fly() { return this.name + " บินได้"; },
};
const canSwim = {
swim() { return this.name + " ว่ายน้ำได้"; },
};
const duck = {
name: "เป็ดโดนัลด์",
...canFly,
...canSwim,
};
duck.fly(); // "เป็ดโดนัลด์ บินได้"
duck.swim(); // "เป็ดโดนัลด์ ว่ายน้ำได้"💡
thisในตัวอย่างนี้คืออะไร: spread...canFlyคัดลอก methodflyมาเป็น property ของduckตรง ๆ → เวลาเรียกduck.fly()thisภายในflyคือduckเอง (กฎ "ใครเรียก ใครเป็น this" จากบทที่ 05) → จึงเข้าถึงthis.nameได้
→ ใช้ inheritance ได้ แต่อย่าสืบทอดลึกเกิน 1-2 ชั้น
Part 7: Map — เก็บคู่ key-value ที่ดีกว่า object บางกรณี
จำได้ไหมว่า key ของ object ที่เรียนมาตั้งแต่ Part 1 นั้น ต้องเป็น string เสมอ (หรือ symbol ซึ่งเป็น type พิเศษ) — นั่นหมายความว่าถ้าเราใส่ตัวเลข เช่น obj[0] JavaScript จะแปลง 0 เป็น string "0" ให้อัตโนมัติ ทำให้ key เป็น object ไม่ได้เลย
Map คือโครงสร้างข้อมูลที่เก็บ key-value เหมือน object แต่แก้ข้อจำกัดนี้ได้:
Map คือโครงสร้างข้อมูลที่เก็บ key-value เหมือน object แต่ดีกว่าในบางด้าน:
javascript
const map = new Map();
map.set("name", "Alice"); // เพิ่ม
map.set("age", 25);
map.get("name"); // "Alice" — อ่าน
map.has("age"); // true — เช็คว่ามีไหม
map.delete("age"); // ลบ
map.size; // 1 — จำนวน
// key เป็นอะไรก็ได้ — แม้แต่ object! (object ทำไม่ได้)
const userKey = { id: 1 };
map.set(userKey, "ข้อมูลของ user 1");วน Map:
javascript
for (const [key, value] of map) {
console.log(key, value);
}ข้อดีของ Map เหนือ object:
- key เป็นชนิดอะไรก็ได้ (object, function ก็เป็น key ได้)
- มี
.size(object ต้องObject.keys(obj).length) - รักษาลำดับการเพิ่ม
Map และ object มีประสิทธิภาพใกล้เคียงกันในงานทั่วไป — เลือก Map เพราะความหมายของโค้ด (semantic) เหมาะกว่า เช่น key ไม่ใช่ string หรือต้องการ .size/รักษาลำดับ ไม่ใช่เพราะเรื่องความเร็ว (performance)
→ ใช้ Map เมื่อ: key ไม่ใช่ string, หรือ เพิ่ม/ลบ key บ่อย / ใช้ object เมื่อ: โครงสร้างข้อมูลที่ key รู้แน่นอน
Part 8: Set — เก็บค่าที่ไม่ซ้ำ
Set เก็บค่า — แต่ "ไม่ซ้ำกัน" (ค่าที่ซ้ำจะถูกตัดออก):
javascript
const set = new Set();
set.add(1);
set.add(2);
set.add(2); // ซ้ำ — ไม่ถูกเพิ่ม
set.add(3);
console.log(set.size); // 3 (ไม่ใช่ 4)
set.has(2); // true
set.delete(1);ประโยชน์ที่ใช้บ่อยสุด — ตัดค่าซ้ำออกจาก array:
javascript
const numbers = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(numbers)];
console.log(unique); // [1, 2, 3, 4]อธิบาย: ใส่ array เข้า Set → ค่าซ้ำถูกตัด → ใช้ spread ... แปลงกลับเป็น array
Part 9: JSON — แปลง object เป็นข้อความ
JSON (JavaScript Object Notation) = รูปแบบข้อความสำหรับเก็บ/ส่งข้อมูล — ใช้ส่งข้อมูลระหว่าง server (เซิร์ฟเวอร์ฝั่งหลังบ้าน) กับ client (ฝั่งผู้ใช้ เช่น browser หรือ mobile app)
9.1 object → JSON string
JSON.stringify() แปลง object ของ JS เป็นข้อความ JSON เพื่อส่งหรือเก็บ — ใส่ argument เพิ่มเพื่อจัดย่อหน้าให้อ่านง่ายได้ (JSON.stringify(obj, null, 2)):
javascript
const user = { name: "Alice", age: 25, active: true };
const json = JSON.stringify(user);
console.log(json); // '{"name":"Alice","age":25,"active":true}'
// แบบจัดรูปแบบสวย (ใส่ย่อหน้า)
JSON.stringify(user, null, 2);9.2 JSON string → object
ในทางกลับกัน JSON.parse() แปลงข้อความ JSON (เช่นที่ได้จาก API) กลับเป็น object ของ JS ที่ใช้งานได้:
javascript
const json = '{"name":"Alice","age":25}';
const obj = JSON.parse(json);
console.log(obj.name); // "Alice"9.3 ⚠️ ข้อจำกัดของ JSON
JSON เก็บได้แค่: string, number, boolean, null, array, object — ไม่รองรับ:
function— หายไปundefined— หายไปDate— กลายเป็น string (parse กลับไม่ได้ Date)Map,Set— กลายเป็น{}(object ว่าง)
javascript
JSON.stringify({ fn: () => {}, val: undefined, when: new Date() });
// '{"when":"2026-..."}' — fn และ val หายไป, when กลายเป็น string⚠️ ระวังเป็นพิเศษ —
Map/Setหายข้อมูลเงียบ ๆ:JSON.stringify(new Map([["a", 1]]))ได้ผลลัพธ์เป็น"{}"— ข้อมูลทุกตัวใน Map หายหมดโดยไม่มี error หรือคำเตือนใด ๆ นี่อันตรายกว่ากรณีundefined/functionเพราะ Map/Set หน้าตาเหมือน object ปกติจนกว่าจะสังเกตดี ๆ — ถ้ามี Map/Set อยู่ใน object ที่จะJSON.stringifyต้องแปลงเป็น array ก่อน (เช่น[...map.entries()]) ไม่งั้นข้อมูลจะหายไปเงียบ ๆ
→ ถ้าต้องการ copy object ที่ซับซ้อน — ใช้ structuredClone() ไม่ใช่ JSON
Part 10: Lab — ลงมือทำ
Lab 1: Class พื้นฐาน
ฝึกสร้าง class ที่มี constructor, getter (area, perimeter) และ method ที่แก้ state (scale) — รวมความรู้พื้นฐานเรื่อง class เข้าด้วยกัน:
javascript
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
get area() { // getter
return this.width * this.height;
}
get perimeter() {
return 2 * (this.width + this.height);
}
scale(factor) {
this.width *= factor;
this.height *= factor;
}
}
const rect = new Rectangle(4, 3);
console.log(rect.area); // 12
console.log(rect.perimeter); // 14
rect.scale(2);
console.log(rect.area); // 48Lab 2: Private + Inheritance
ฝึกรวม private field (#balance) กับ inheritance — class ลูก (SavingsAccount) ใช้ method ของแม่ได้แต่แตะ private ของแม่โดยตรงไม่ได้ ต้องผ่าน method สาธารณะ:
javascript
class Account {
#balance = 0;
deposit(amount) {
if (amount <= 0) throw new Error("จำนวนเงินต้องมากกว่า 0");
this.#balance += amount;
}
get balance() {
return this.#balance;
}
}
class SavingsAccount extends Account {
#interestRate;
constructor(rate) {
super();
this.#interestRate = rate;
}
addInterest() {
this.deposit(this.balance * this.#interestRate);
}
}
const savings = new SavingsAccount(0.05);
savings.deposit(1000);
savings.addInterest();
console.log(savings.balance); // 1050Lab 3: Set ตัดค่าซ้ำ
ฝึกใช้ Set ร่วมกับ spread เพื่อตัดค่าซ้ำออกจาก array ในบรรทัดเดียว — สำนวนที่ใช้บ่อยมากในงานจริง:
javascript
const tags = ["js", "web", "js", "node", "web", "react"];
const uniqueTags = [...new Set(tags)];
console.log(uniqueTags); // ["js", "web", "node", "react"]Lab 4: Map นับความถี่
javascript
const words = ["a", "b", "a", "c", "a", "b"];
const count = new Map();
for (const word of words) {
count.set(word, (count.get(word) ?? 0) + 1);
}
for (const [word, n] of count) {
console.log(`${word}: ${n}`); // a: 3, b: 2, c: 1
}Part 11: Checkpoint
- เข้าถึง property ด้วย
.กับ[ ]ต่างกันยังไง? ใช้อันไหนเมื่อไหร่? Object.keys,Object.values,Object.entriesคืนอะไร?- Prototype chain ทำงานยังไง?
classเป็นของจริงหรือเป็นแค่ "เปลือก" ของอะไร?constructorทำอะไร?- Private field (
#) ทำอะไร? staticต่างจาก method ปกติยังไง?extendsกับsuperทำงานยังไง?- ทำไมแนะนำ "composition over inheritance"?
- Map ดีกว่า object ตรงไหน? Set ใช้ทำอะไร?
Part 12: สรุปบทนี้
- Object = กลุ่มข้อมูลแบบ key-value — เข้าถึงด้วย
.หรือ[ ] Object.keys/values/entries= แปลง object เป็น array เพื่อวน- Prototype chain = กลไกหา property — หาตัวเอง ไม่เจอ → หา prototype ต่อ ๆ ไป
class= เปลือกสวยงามของ prototype —constructor, method,#private,static, getter/setter- Inheritance (
extends,super) — สืบทอดได้ แต่อย่าลึกเกินไป — เลือก composition Map= key-value ที่ key เป็นอะไรก็ได้ /Set= ค่าที่ไม่ซ้ำ- JSON = แปลง object ↔ string — แต่ไม่รองรับ function/undefined/Date
บทต่อไป — Array และเทคนิคจัดการข้อมูล (map/filter/reduce)