JavaScript
Class
Static Methods & Properties
เรียนรู้การใช้ static keyword เพื่อสร้างเมธอดและ property ที่เป็นของคลาส — เรียกผ่านชื่อคลาสโดยตรง โดยไม่ต้องสร้าง instance ตั้งแต่ utility function, factory method, ตัวนับกลาง ไปจนถึงกฎสำคัญและข้อผิดพลาดที่พบบ่อย ผ่าน Lab 5 ข้อ
static คืออะไร — เมธอดที่เป็นของ class ไม่ใช่ของ instance
**`static`** คือ keyword ที่ใช้ประกาศ method หรือ property ให้เป็นของ **class เอง** — แทนที่จะเป็นของ instance ความแตกต่างสำคัญ: - **Instance method** — เรียกผ่าน instance ที่สร้างจาก `new` — ต้องมี object ก่อนถึงจะใช้ได้ — `instance.method()` - **Static method** — เรียกผ่าน class โดยตรง — ไม่ต้องสร้าง instance — `ClassName.method()` เปรียบง่าย ๆ: instance method คือพฤติกรรมของ "แต่ละคน" ส่วน static method คือเครื่องมือของ "บริษัท" — ใครจะใช้ก็เรียกผ่านชื่อบริษัทได้เลย
`greet()` เป็น instance method — เรียกผ่าน instance `add()` เป็น static method — เรียกผ่าน class โดยตรง
class Calculator {
// instance method — ต้องมี instance ก่อนถึงใช้ได้
multiply(a, b) {
return a * b;
}
// static method — เรียกผ่าน class ได้เลย
static add(a, b) {
return a + b;
}
}
// static method — เรียกผ่าน class โดยตรง
console.log(Calculator.add(3, 5)); // 8
// instance method — ต้องสร้าง instance ก่อน
const calc = new Calculator();
console.log(calc.multiply(3, 5)); // 15จากตัวอย่าง: - `Calculator.add(3, 5)` — เรียก static method ผ่านชื่อ class โดยตรง — ไม่ต้องใช้ `new` - `calc.multiply(3, 5)` — เรียก instance method ผ่าน instance — ต้องสร้าง `const calc = new Calculator()` ก่อน **วิธีสังเกต**: static method ใช้ `static` keyword นำหน้า — `static methodName() { }` — ส่วน instance method ไม่มี `static` — `methodName() { }`
`this` ใน static method — ชี้ไปที่ class ไม่ใช่ instance
ใน instance method — `this` ชี้ไปที่ instance ที่เรียก method นั้น ใน static method — `this` ชี้ไปที่ **class เอง** (เพราะไม่มี instance) ผลลัพธ์: static method **ไม่สามารถเข้าถึง instance property** (`this.name`, `this.price`) — แต่มันเข้าถึง static property และ static method อื่น ๆ ใน class ได้
`this` ใน instance method = instance `this` ใน static method = class Static method เข้าถึง `this.name` ไม่ได้
class User {
constructor(name) {
this.name = name; // instance property
}
// instance method — this = instance
greet() {
return "สวัสดี " + this.name;
}
// static method — this = class (User)
static getType() {
return "นี่คือคลาส User";
// ❌ this.name — undefined เพราะ static method
// ไม่มี instance property name
}
}
const u = new User("สมชาย");
console.log(u.greet()); // "สวัสดี สมชาย" — this = u
console.log(User.getType()); // "นี่คือคลาส User" — this = Userstatic method ใช้ `this` เพื่อเรียก static method หรือ static property อื่นใน class
class Config {
static appName = "MyApp";
static version = "1.0.0";
static getAppInfo() {
// this.appName = Config.appName (this = class)
return this.appName + " v" + this.version;
}
}
console.log(Config.getAppInfo()); // "MyApp v1.0.0"กฎสำคัญ: static method ห้ามใช้ `this.propertyName` เพื่อเข้าถึง instance property — เพราะ `this` เป็น class — instance property ยังไม่ถูกสร้างจนกว่า `new` จะรัน แต่ static method ใช้ `this` เพื่อเข้าถึง static member อื่นใน class ได้ — นี่คือวิธี reuse static logic ภายใน class
เมื่อไหร่ควรใช้ static method — 3 use-case หลัก
Static method มีประโยชน์ใน 3 สถานการณ์หลัก: 1. **ฟังก์ชันช่วยเหลือ (utility)** — logic ที่เกี่ยวกับคลาส แต่ไม่ต้องใช้ข้อมูลของ instance — เช่น `Math.max()`, `Array.isArray()` 2. **Factory method** — สร้าง instance จากข้อมูลที่ซับซ้อน — เช่น `Date.parse()`, `Number.parseInt()` 3. **ตัวนับ หรือข้อมูลกลาง** — ข้อมูลที่แชร์ร่วมกันทั้งหมด — เช่น จำนวน instance ทั้งหมดที่ถูกสร้าง
`Validator.isEmail()` — ตรวจสอบว่า input เป็น email หรือไม่ โดยไม่ต้องสร้าง instance
class Validator {
static isEmail(input) {
return input.includes("@") && input.includes(".");
}
static isNumber(input) {
return !Number.isNaN(Number(input));
}
}
console.log(Validator.isEmail("user@mail.com")); // true
console.log(Validator.isEmail("not-email")); // false
console.log(Validator.isNumber("123")); // true
console.log(Validator.isNumber("abc")); // false`Point.fromArray([x, y])` รับ array แล้วแปลงเป็น Point instance — สะดวกกว่าสร้างผ่าน constructor ตรง ๆ
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
// factory method — สร้าง Point จาก array [x, y]
static fromArray(arr) {
return new Point(arr[0], arr[1]);
}
// factory method — สร้าง Point จาก object { x, y }
static fromObject(obj) {
return new Point(obj.x, obj.y);
}
}
const p1 = Point.fromArray([10, 20]);
const p2 = Point.fromObject({ x: 5, y: 15 });
console.log(p1.x, p1.y); // 10 20
console.log(p2.x, p2.y); // 5 15`Counter.count` — นับจำนวน instance ทั้งหมดที่ถูกสร้าง — ทุก instance บวก 1 เข้า static property ตัวเดียวกัน
class Player {
static totalPlayers = 0; // static property — ตัวนับกลาง
constructor(name) {
this.name = name;
// ทุกครั้งที่สร้าง instance — บวก 1 เข้า static property
Player.totalPlayers = Player.totalPlayers + 1;
}
static getTotal() {
return "จำนวนผู้เล่นทั้งหมด: " + Player.totalPlayers;
}
}
const p1 = new Player("สมชาย");
const p2 = new Player("สมหญิง");
const p3 = new Player("สมปอง");
console.log(Player.getTotal()); // "จำนวนผู้เล่นทั้งหมด: 3"**วิธีคิด**: ถ้า logic นั้น**ไม่จำเป็นต้องรู้ว่า instance เป็นใคร** (ไม่ต้องใช้ `this.name`, `this.price`) — static method อาจเหมาะสม **ข้อควรระวัง**: อย่าใช้ static method แทน instance method เมื่อ logic ต้องเข้าถึง property ของ instance — static method เป็นของคลาส — instance property เป็นของแต่ละ instance — ไม่ควรเรียกข้ามกัน
static properties — ข้อมูลระดับ class
นอกจาก static method แล้ว — `static` ยังใช้กับ **property** ได้ด้วย Static property คือ property ที่เป็นของ class — ไม่ใช่ของ instance — ทุก instance มองเห็นและแชร์ค่าเดียวกัน **ประโยชน์**: เก็บค่า configuration, default value, ตัวนับ, หรือค่าคงที่ที่เกี่ยวข้องกับคลาส
`Currency.defaultCurrency` คือค่า default ที่ทุก instance แชร์ร่วมกัน
class Currency {
static defaultCurrency = "THB"; // static property
constructor(amount) {
this.amount = amount;
}
format() {
return this.amount + " " + Currency.defaultCurrency;
}
}
const price = new Currency(500);
console.log(price.format()); // "500 THB"
// เปลี่ยนค่า default — ทุก instance ใช้ค่าใหม่
Currency.defaultCurrency = "USD";
console.log(price.format()); // "500 USD"`User.MAX_NAME_LENGTH` — ใช้เป็นค่าคงที่สำหรับ validate
class User {
static MAX_NAME_LENGTH = 20;
static MIN_PASSWORD_LENGTH = 8;
constructor(name) {
if (name.length > User.MAX_NAME_LENGTH) {
console.log(
"ชื่อต้องไม่เกิน " + User.MAX_NAME_LENGTH + " ตัวอักษร"
);
}
this.name = name;
}
}
const u1 = new User("สมชาย"); // OK
const u2 = new User("ชื่อที่ยาวเกิน 20 ตัวอักษร"); // เตือน**การเข้าถึง static property จากภายนอก**: `ClassName.propertyName` — เช่น `User.MAX_NAME_LENGTH` **การเข้าถึง static property จากภายใน**: ใช้ `ClassName.propertyName` หรือ `this.constructor.propertyName` ใน instance method — หรือใช้ `this.propertyName` ใน static method (เพราะ `this` = class)
กฎสำคัญของ static
| กฎ | คำอธิบาย | ตัวอย่าง |
|---|---|---|
| ใช้ `static` keyword นำหน้า | method หรือ property ต้องมี `static` นำหน้า `static methodName() { }` / `static prop = value` | `static add() {}` ✅ / `add() {}` (instance) ❌ |
| เรียกผ่าน class — ไม่ใช่ instance | static member เรียกผ่าน `ClassName.method()` — เรียกผ่าน instance จะ error หรือเป็น `undefined` | `Math.max(3, 5)` ✅ / `instance.max(3, 5)` ❌ |
| `this` ใน static method = class | `this` ใน static method ชี้ไปที่ class — ไม่ใช่ instance — เข้าถึง instance property (`this.name`) ไม่ได้ | `this.appName` — static property / `this.name` — undefined |
| เข้าถึง instance property ไม่ได้ | static method ไม่มี instance ที่มันผูกอยู่ — property ที่สร้างใน constructor (`this.name`) ไม่มีใน static context | `static greet() { return this.name; }` — undefined |
| static member แชร์ร่วมกันทุก instance | เปลี่ยนค่า static property จากที่ไหน — ทุกคนที่อ่านค่า static นั้นจะเห็นค่าใหม่ทันที | `ClassName.LIMIT = 100` — เปลี่ยนที่เดียว มีผลทุกที่ |
| instance method เข้าถึง static ได้ | instance method เข้าถึง static member ผ่าน `ClassName.method()` หรือ `this.constructor.method()` — แต่ static method เข้าถึง instance member ไม่ได้ | `obj.method()` → เรียก static ได้ / `Class.static()` → เรียก instance ไม่ได้ |
ข้อผิดพลาดที่พบบ่อย
- **เรียก static method ผ่าน instance**: `const obj = new MyClass(); obj.staticMethod()` — จะ error เพราะ static method อยู่ใน class ไม่ใช่ instance — ต้องเรียก `MyClass.staticMethod()`
- **ใช้ `this.name` ใน static method**: `this` ใน static method คือ class — `this.name` ไม่ใช่ instance property — จะได้ `undefined` — ถ้าอยากใช้ instance property ให้เปลี่ยนเป็น instance method หรือส่ง instance เข้ามาเป็น parameter
- **ลืมใส่ `static` keyword**: เขียน `add() { }` แทนที่จะเป็น `static add() { }` — method จะกลายเป็น instance method — เรียกผ่าน class จะได้ error — `ClassName.add is not a function`
- **static method พยายามเข้าถึง instance property ที่ไม่มี**: ใน static method `this` คือ class — `this.name` ไม่มีอยู่จริง — ค่าจะเป็น `undefined` หรือ error — ต้องแน่ใจว่า property ที่ใช้ใน static method เป็น static property
- **ใส่ `static` หน้า constructor**: `static constructor()` — constructor เป็น static ไม่ได้ — JavaScript จะ throw `SyntaxError` — constructor ถูกออกแบบให้เป็น instance method เสมอ
- **คิดว่า static property เป็น private**: `static LIMIT = 100` — static property ถูกอ่านและแก้ไขจากภายนอกได้ตามปกติ — `ClassName.LIMIT = 999` — ถ้าอยากจำกัดการเข้าถึง ให้ใช้ `#` private fields (เรียนในบทถัดไป)
- **ใช้ static มากเกินไป**: ไม่ใช่ทุกอย่างต้องเป็น static — static method ไม่มี `this` instance — ถ้า logic ต้องใช้ข้อมูลของ instance (name, price, score) ควรใช้ instance method — static ควรใช้กับ utility, factory, และข้อมูลกลางเท่านั้น