JavaScript hỗ trợ lập trình hướng đối tượng qua prototype và cú pháp class. Bài này giải thích bản chất prototype, rồi dùng class hiện đại với trường private, static, getter/setter và kế thừa.
- Hiểu prototype và chuỗi prototype
- Viết class với constructor, phương thức, getter/setter
- Dùng trường #private và static
- Kế thừa với extends, super
- Prototype
- Đối tượng mẫu mà đối tượng khác kế thừa thuộc tính.
- Instance
- Đối tượng tạo ra từ class bằng new.
- #private
- Trường riêng tư thật, không truy cập được từ bên ngoài.
- static
- Thành viên thuộc về class, không thuộc từng đối tượng.
Khái niệm và lý thuyết
Khác Java hay C#, JavaScript dựa trên prototype: mỗi đối tượng có liên kết ẩn tới một đối tượng khác (prototype). Khi truy cập thuộc tính không có, JS tìm tiếp lên chuỗi prototype. Cú pháp class (ES2015) chỉ là cách viết gọn hơn trên cơ chế này, nhưng nay đã có thêm tính năng riêng như trường #private, khối static {}.
Bốn nguyên lý OOP vẫn áp dụng: đóng gói (#private), kế thừa (extends), đa hình (ghi đè phương thức), trừu tượng (ẩn chi tiết sau phương thức công khai).
Prototype
const dongVat = { an() { return this.ten + " đang ăn"; } };
const meo = Object.create(dongVat);
meo.ten = "Mướp";
console.log(meo.an()); // tìm an() trên prototype
console.log(Object.getPrototypeOf(meo) === dongVat); // trueClass hiện đại
class TaiKhoan {
#soDu = 0; // private thật
static laiSuat = 0.05; // dùng chung
constructor(chuTK) { this.chuTK = chuTK; }
napTien(x) {
if (x <= 0) throw new RangeError("Số tiền phải dương");
this.#soDu += x;
return this; // cho phép gọi nối chuỗi
}
get soDu() { return this.#soDu; }
static tao(ten) { return new TaiKhoan(ten); }
}
const tk = TaiKhoan.tao("An").napTien(100).napTien(50);
console.log(tk.soDu); // 150
// tk.#soDu -> SyntaxErrorKế thừa và đa hình
class Hinh {
dienTich() { return 0; }
toString() { return `${this.constructor.name}: ${this.dienTich().toFixed(2)}`; }
}
class Tron extends Hinh {
constructor(r) { super(); this.r = r; }
dienTich() { return Math.PI * this.r ** 2; }
}
class Vuong extends Hinh {
constructor(a) { super(); this.a = a; }
dienTich() { return this.a ** 2; }
}
[new Tron(1), new Vuong(2)].forEach(h => console.log(String(h)));Ưu tiên kết hợp (composition) hơn kế thừa nhiều tầng: một lớp chứa đối tượng khác thường linh hoạt hơn chuỗi kế thừa dài.
Truyền phương thức làm callback sẽ mất this (ví dụ setTimeout(tk.napTien, 0)). Dùng hàm mũi tên hoặc bind.
- Viết class
SinhViencó #diem là mảng, phương thức themDiem, getter diemTB. - Tạo class
SinhVienCNTTkế thừa, ghi đè phương thức xepLoai. - Viết phương thức static
tuJSON(obj)tạo đối tượng từ dữ liệu JSON. - Kiểm tra chuỗi prototype bằng
instanceofvàObject.getPrototypeOf.
- JS dựa trên prototype; class là cú pháp gọn.
- #private, static, get/set có sẵn trong class.
- extends và super cho kế thừa.
- Cẩn thận mất this khi truyền phương thức.