Bài 9. Lớp và lập trình hướng đối tượng

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.

50 phútTrung cấp
Bạn sẽ học được
  • 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
Thuật ngữ cần biết
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); // true

Class 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 -> SyntaxError

Kế 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)));
Mẹo hay

Ư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.

Lưu ý

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.

Thực hành
  1. Viết class SinhVien có #diem là mảng, phương thức themDiem, getter diemTB.
  2. Tạo class SinhVienCNTT kế thừa, ghi đè phương thức xepLoai.
  3. Viết phương thức static tuJSON(obj) tạo đối tượng từ dữ liệu JSON.
  4. Kiểm tra chuỗi prototype bằng instanceof và Object.getPrototypeOf.
Tóm tắt bài học
  • 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.