OOP, Prototype & Module
ES6 เพิ่งเพิ่มคีย์เวิร์ด class เข้ามา — เบื้องหลังจริง ๆ JS ยังทำงานด้วย
prototype อยู่ ซึ่งเป็นระบบ OOP ที่ต่างจาก Java
class & constructor
class Car {
constructor(name, year) {
this.name = name;
this.year = year;
}
}
const myCar1 = new Car("Ford", 2014);
const myCar2 = new Car("Audi", 2019);
ใน JS ใช้คีย์เวิร์ด
constructor ตรง ๆ ไม่ต้องตั้งชื่อเหมือนคลาสแบบ Javamethod & parameter
class Car {
constructor(name, year) {
this.name = name;
this.year = year;
}
age(x) {
return x - this.year;
}
}
const myCar = new Car("Ford", 2014);
myCar.age(2026); // 12
extends & super
function CarFunc(name) {
this.name = name;
}
class CarClass extends CarFunc {
constructor(name, brand) {
super(name); // เรียก constructor ของแม่ก่อน
this.carName = brand;
}
}
ต้องเรียก
super() ก่อนใช้ this ในคลาสลูกเสมอEncapsulation ด้วย #
class BankAccount {
#balance = 0; // private field
deposit(amount) {
this.#balance = this.#balance + amount;
}
withdraw(amount) {
if (amount <= this.#balance) {
this.#balance = this.#balance - amount;
} else {
console.log("Insufficient funds");
}
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount();
account.deposit(1000);
account.withdraw(500);
console.log(account.getBalance()); // 500
// console.log(account.#balance); // Error — เข้าถึงจากนอกคลาสไม่ได้
JS ไม่มี method overloading
Java ตั้งชื่อเมธอดซ้ำได้ถ้าพารามิเตอร์ต่างกัน แต่ JS ทำไม่ได้ — ใช้ ad-hoc overloading แทน
function foo(a, b) {
switch (arguments.length) {
case 0: // ไม่มี argument
break;
case 1: // ทำงานกับ a
break;
case 2:
default: // ทำงานกับ a และ b
break;
}
}
// หรือเก็บฟังก์ชันไว้ใน array ตามจำนวน argument
fooArr = [
function () {},
function (a) {},
function (a, b) {}
];
function foo(a, b) {
return fooArr[arguments.length](a, b);
}
JS ไม่มี generic
เพราะตัวแปรเป็น dynamic type ไม่ต้องประกาศชนิด ถ้าต้องการ <T> จริง ๆ ต้องไปใช้
TypeScript
Prototype & prototype chain
ก่อนมี class ใน ES6 การสร้าง object ใน JS ใช้ function constructor + prototype
function Car(name) { // function constructor
this.name = name;
}
let Car1 = new Car('John');
console.log(Car1.__proto__ === Car.prototype); // true
Object.prototype อยู่บนสุดของห่วงโซ่การสืบทอด
เพิ่ม property ทีหลัง — ใช้ .prototype หรือไม่ใช้?
function CarFunc(name) { this.name = name; }
class CarClass extends CarFunc {
constructor(name, brand) { super(name); this.carName = brand; }
}
let Car2 = new CarClass('John', 'toyota');
// กรณีที่ 1 — ใช้ .prototype
CarFunc.prototype.color = "green";
console.log(Car2.color); // "green" ← ไหลตาม chain ลงมาถึงลูก
// กรณีที่ 2 — ไม่ใช้ .prototype
CarFunc.color = "green";
console.log(Car2.color); // undefined ← ไม่ได้อยู่ใน chain
สรุป: ถ้าอยากให้ object ที่สร้างจากคลาสนั้น (รวมถึงคลาสลูก) เห็น property ที่เพิ่มทีหลัง
ต้องเพิ่มผ่าน
.prototype เท่านั้นclass กับ JSON
class MyClass {
constructor() { this.foo = 3; }
}
const myClass = new MyClass();
console.log(JSON.stringify(myClass)); // {"foo":3}
let myjson = { foo: 3 };
console.log(JSON.stringify(myjson)); // {"foo":3} ← เหมือนกัน
object ที่สร้างจาก class กับ object ธรรมดา เมื่อ stringify แล้วได้ผลเหมือนกัน —
เพราะสุดท้ายมันก็คือ JavaScript Object ทั้งคู่ (function จะหายไปตอน stringify)
JS Module
แบ่งโค้ดออกเป็นไฟล์ย่อยแล้วเรียกใช้ข้ามไฟล์
1. export ตัวแปรหลายตัว
// person.js const name = "Jesse"; const age = 40; export { name, age }; // ไฟล์ที่เรียกใช้ <script type="module"> import { name, age } from "./person.js"; console.log(name); </script>
2. export default (function module)
// person.js const person = () => { const name = "Jesse"; const age = 40; return name + ' is ' + age + ' years old.'; }; export default person; // ไฟล์ที่เรียกใช้ <script type="module"> import person from "./person.js"; console.log(person()); </script>
3. class module
// person.js
class person {
constructor() {}
helloworld() { console.log('hello world'); }
}
export default person;
| named export | default export | |
|---|---|---|
| เขียน export | export { name, age }; | export default person; |
| เขียน import | import { name } from './person.js' — ต้องใส่ปีกกาและชื่อตรง | import person from './person.js' — ตั้งชื่ออะไรก็ได้ |
| มีได้กี่ตัวต่อไฟล์ | หลายตัว | 1 ตัว |
แท็ก script ที่ import module ต้องใส่
type="module" เสมอReact ยุคใหม่นิยมใช้ function module เป็นหลักมากกว่า class module
เพื่อลดปัญหาการจัดการ state