JavaScript
Class
Inheritance (extends / super)
เรียนรู้การสืบทอดคลาสด้วย extends การเรียก constructor ของ parent ผ่าน super() การ override method และการใช้ super.method() เพื่อขยายพฤติกรรมของ parent
Inheritance คืออะไร — extends
Inheritance (การสืบทอด) คือกลไกที่ทำให้คลาสหนึ่งรับ property และ method ทั้งหมดมาจากอีกคลาสหนึ่ง โดยใช้ keyword `extends` คลาสที่ถูกสืบทอดเรียกว่า **parent class** (หรือ superclass) และคลาสที่สืบทอดมาเรียกว่า **child class** (หรือ subclass) ประโยชน์หลัก: เขียนโค้ดครั้งเดียวที่ parent แล้ว child ทุกตัวใช้ร่วมกันได้ — ไม่ต้องประกาศ method ซ้ำในทุกคลาส
Dog extends Animal — Dog ได้ method speak() มาจาก Animal โดยไม่ต้องประกาศเอง
class Animal {
speak() {
return "เสียงสัตว์";
}
}
class Dog extends Animal {
// Dog ไม่มี speak() ของตัวเอง
// แต่เรียกใช้ได้เพราะ inherit มาจาก Animal
}
const dog = new Dog();
console.log(dog.speak()); // "เสียงสัตว์" — ได้มาจาก Animalจากตัวอย่าง: `class Dog extends Animal` หมายความว่า Dog สืบทอดทุกอย่างจาก Animal — Property และ Method ทั้งหมดใน Animal จะมีใน Dog ด้วย โดย Dog ไม่ต้องประกาศซ้ำ **คิดภาพ**: Animal เป็นพิมพ์เขียวของ "สัตว์ทุกชนิด" — Dog ก็คือ Animal ชนิดหนึ่ง จึงมีพฤติกรรมพื้นฐานร่วมกัน แต่สามารถเพิ่มหรือปรับแต่งพฤติกรรมเฉพาะของตัวเองได้ (ซึ่งจะเรียนในหัวข้อถัดไป)
super() — เรียก constructor ของ parent
เมื่อ parent class มี constructor ที่รับ parameter — child class ต้องเรียก `super()` เพื่อส่งต่อค่าไปให้ parent constructor ทำงาน **กฎสำคัญ**: ถ้า child class มี constructor ต้องเรียก `super()` ก่อนถึงจะใช้ `this` ได้ — JavaScript จะ报 error ถ้าไม่ทำตามกฎนี้
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return this.name + " ส่งเสียง";
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // เรียก Animal constructor — ต้องมาก่อน this
this.breed = breed; // property เฉพาะของ Dog
}
describe() {
return this.name + " เป็น " + this.breed;
}
}
const dog = new Dog("Rex", "German Shepherd");
console.log(dog.speak()); // "Rex ส่งเสียง" — method จาก parent
console.log(dog.describe()); // "Rex เป็น German Shepherd" — method ของตัวเองสังเกตลำดับการทำงาน: 1. `new Dog("Rex", "German Shepherd")` เรียก constructor ของ Dog 2. `super(name)` เรียก constructor ของ Animal — ตั้งค่า `this.name = "Rex"` 3. หลังจาก `super()` ทำงานเสร็จ Dog ถึงจะตั้งค่า `this.breed = "German Shepherd"` **ถ้าสลับบรรทัด** — ใช้ `this.breed` ก่อน `super()` — JavaScript จะ throw `ReferenceError`
การ override method — เขียน method ชื่อเดียวกับ parent
Override คือการเขียน method ชื่อเดียวกับ parent ลงใน child class — เมื่อเรียก method นั้น JavaScript จะใช้เวอร์ชันของ child แทน ใช้ override เมื่อ child ต้องการพฤติกรรมที่แตกต่างจาก parent โดยไม่ต้องเปลี่ยนโค้ดที่ parent
class Animal {
speak() {
return "เสียงสัตว์";
}
}
class Dog extends Animal {
speak() {
return "โฮ่ง โฮ่ง"; // override — แทนที่ speak() ของ Animal
}
}
class Cat extends Animal {
speak() {
return "เหมียว เหมียว"; // override — แทนที่ speak() ของ Animal
}
}
const dog = new Dog();
const cat = new Cat();
console.log(dog.speak()); // "โฮ่ง โฮ่ง" — ใช้เวอร์ชันของ Dog
console.log(cat.speak()); // "เหมียว เหมียว" — ใช้เวอร์ชันของ Catจากตัวอย่าง: `Animal` มี `speak()` ต้นแบบ — `Dog` และ `Cat` ต่าง override `speak()` ให้คืนค่าเฉพาะของตัวเอง **กฎการค้นหา method**: JavaScript ดูที่ child ก่อน — ถ้าพบ ใช้ของ child — ถ้าไม่พบ จึงค่อยไล่ขึ้นไปดูที่ parent
super.method() — เรียก method ของ parent จาก child
บางครั้งเราไม่ได้อยากแทนที่ parent method ทั้งหมด — แค่อยาก**เพิ่ม**พฤติกรรมต่อจากของเดิม ใช้ `super.methodName()` เพื่อเรียก method ของ parent แล้วค่อยเพิ่ม logic ของ child ต่อ
class Character {
constructor(name, level) {
this.name = name;
this.level = level;
}
describe() {
return this.name + " เลเวล " + this.level;
}
}
class Warrior extends Character {
constructor(name, level, weapon) {
super(name, level);
this.weapon = weapon;
}
describe() {
// เรียก describe() ของ Character แล้วเพิ่ม weapon ต่อท้าย
return super.describe() + " ใช้อาวุธ " + this.weapon;
}
}
const warrior = new Warrior("Arthur", 10, "ดาบ");
console.log(warrior.describe());
// "Arthur เลเวล 10 ใช้อาวุธ ดาบ"`super.describe()` เรียก `describe()` ของ parent (Character) — ได้ผลลัพธ์ `"Arthur เลเวล 10"` — แล้ว Warrior ต่อท้าย `" ใช้อาวุธ ดาบ"` **เมื่อไหร่ควรใช้ `super.method()`**: เมื่อ child อยากขยายพฤติกรรมของ parent — ไม่ใช่แทนที่ทั้งหมด — ทำให้ child ได้ทั้งของเดิมและของใหม่
instanceof — ตรวจสอบสายสกุลของ object
`instanceof` เป็น operator ที่ใช้ตรวจสอบว่า object ถูกสร้างจากคลาสไหน (หรือคลาสที่สืบทอดมา) child instance จะเป็น `instanceof` ทั้งคลาสของตัวเองและคลาส parent ทุกตัวในสาย inheritance
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
const dog = new Dog();
const cat = new Cat();
console.log(dog instanceof Dog); // true — dog มาจาก Dog
console.log(dog instanceof Animal); // true — Dog extends Animal
console.log(dog instanceof Cat); // false — Dog ไม่เกี่ยวกับ Cat| คำถาม | ผลลัพธ์ | เหตุผล |
|---|---|---|
| `dog instanceof Dog` | `true` | dog ถูกสร้างจาก `new Dog()` โดยตรง |
| `dog instanceof Animal` | `true` | Dog extends Animal — dog เป็น Animal ด้วย |
| `dog instanceof Object` | `true` | ทุก class extends Object โดย default |
| `dog instanceof Cat` | `false` | dog ไม่ได้มาจาก Cat และ Cat ไม่ใช่ parent ของ Dog |
ข้อผิดพลาดที่พบบ่อย
- **ลืมเรียก `super()` ใน constructor ของ child**: ถ้า child มี constructor แต่ไม่เรียก `super()` — JavaScript จะ throw `ReferenceError: Must call super constructor before using 'this'`
- **เรียก `this` ก่อน `super()`**: ใน constructor ของ child ต้องเรียก `super()` ก่อน ถึงจะใช้ `this` ได้ — สลับบรรทัดไม่ได้
- **คิดว่า child ต้องมี constructor เสมอ**: ถ้า child ไม่มี constructor JavaScript จะเรียก constructor ของ parent ให้โดยอัตโนมัติ — ไม่ต้องเขียน constructor เปล่า ๆ ที่มีแค่ `super()`
- **override แล้วลืมใส่ logic ของ parent**: ถ้า override method แล้วไม่เรียก `super.method()` — method ของ parent จะไม่ทำงานเลย — ถ้าอยากได้ทั้งของเดิมและของใหม่ ต้องใช้ `super.method()`
- **เข้าใจผิดว่า `extends` ก็อปปี้โค้ด**: `extends` ไม่ได้ copy-paste โค้ดจาก parent มาใส่ child — แต่สร้างสาย inheritance ที่ JavaScript ค้นหา method ตอน runtime — ถ้าเปลี่ยน method ที่ parent ทีหลัง child ก็จะได้รับผลนั้นด้วย
- **ใช้ `super()` ใน method ธรรมดา**: `super()` ใช้เฉพาะใน constructor เท่านั้น — ใน method ธรรมดาต้องใช้ `super.methodName()` — อย่าสับสนระหว่างสองแบบ