Ở tập trước, bạn đã cho mèo kế thừa khả năng, kiểm soát bí mật bằng private field, xài getter/setter như lễ tân hoàng gia. Nhưng class cũng như mèo: cứ tưởng đã hiểu hết nhưng...


🧩 Vấn đề: Mèo con cần học tiếng... từ mẹ

Tưởng tượng bạn có một dòng họ mèo truyền đời: đời mẹ truyền lại tiếng kêu, đời con cải biên. Bạn thử:

class MeoMe {
  keu() {
    console.log('MeoMe: Meo...meo...');
  }
}

class MeoCon extends MeoMe {
  keu() {
    console.log('MeoCon: Meo meo remix!');
    // Gọi lại tiếng gốc
    super.keu();
  }
}

const con = new MeoCon();
con.keu();

Và nó chạy như ý:

MeoCon: Meo meo remix!
MeoMe: Meo...meo...

✅ Giải pháp: Gọi cha bằng super

Người thông minh như bạn chắc chắn nghĩ ra rằng:

“Nếu kế thừa rồi thì phải có cách gọi lại hàm của cha chứ?”

Đúng vậy! Từ khóa super chính là cánh cổng kết nối lên thế hệ trước:

  • Trong constructor → gọi hàm khởi tạo cha
  • Trong method → gọi lại phương thức cha đã bị override
class Meo {
  constructor(ten) {
    this.ten = ten;
  }
}

class MeoCon extends Meo {
  constructor(ten) {
    super(ten); // Không có dòng này là toang
    console.log(`Chào, tôi là ${this.ten}`);
  }
}

🧩 Vấn đề tiếp theo: Làm sao biết dòng dõi của mèo?

Bạn có nhiều loại mèo:

  • MeoTay
  • MeoTa
  • MeoFake
  • MeoMộtPhút

Bạn nhận được một con mèo, nhưng muốn chắc chắn nó thuộc giống gì. Bạn thử:

if (meo instanceof MeoTa) {
  console.log('Chính hiệu MeoTa!');
}

✅ Giải pháp: Kiểm tra huyết thống bằng instanceof

Người thông minh như bạn nghĩ ngay:

“Cần test xem một con mèo là con cháu ai.”

Từ khóa instanceof chính là giấy khai sinh trong JS:

meo instanceof MeoTa; // true nếu meo sinh ra từ class MeoTa hoặc con cháu của nó

Lưu ý: nếu object được clone thủ công kiểu “hàng chợ” thì instanceof có thể bị đánh lừa. Nhưng trong hầu hết trường hợp “mèo nhà lành”, bạn xài được vô tư.


🧩 Vấn đề cuối: Gọi mèo mà mèo không nghe

Tưởng tượng bạn gắn nút trên web để gọi mèo:

class Meo {
  constructor(ten) {
    this.ten = ten;
  }

  keu() {
    console.log(`${this.ten}: Meo meo!`);
  }
}

const meo = new Meo('Tom');
document.getElementById('btn').addEventListener('click', meo.keu);

Bạn click.

Không tiếng mèo. Chỉ có tiếng lỗi:

Cannot read properties of undefined (reading 'ten')

🐱 Vấn đề: this phản chủ

Bạn vừa bị cú "mất gốc this" – một trong những chiêu khó chịu nhất trong JS.

Khi bạn truyền method keu mà không gọi nó (()), bạn không mang theo this. Và this trôi mất như mèo leo hàng rào – về đâu không rõ.


✅ Giải pháp: Trị this bằng cách buộc nó ở nhà

Người thông minh như bạn chắc chắn nghĩ ra rằng:

“Phải làm sao để this luôn trỏ đúng object của mình?”

Có nhiều cách “trói” this:

1. Dùng arrow function trong constructor

class Meo {
  constructor(ten) {
    this.ten = ten;
    this.keu = () => {
      console.log(`${this.ten}: Meo meo!`);
    };
  }
}

2. Dùng .bind(this) để khóa chặt

class Meo {
  constructor(ten) {
    this.ten = ten;
    this.keu = this.keu.bind(this);
  }

  keu() {
    console.log(`${this.ten}: Meo meo!`);
  }
}

3. Dùng arrow function ở chỗ gọi

btn.addEventListener('click', () => meo.keu());

Nhẹ, nhanh, gọn – nhưng không dùng được nếu bạn cần .removeEventListener.


🧾 Tổng kết tập 3: Dòng dõi - Huyết thống - Quyền kiểm soát

Tính năng Công dụng
super Gọi method hoặc constructor từ class cha
instanceof Kiểm tra object có thuộc class nào đó không
this trong callback Cần “trị” để không bị lạc gốc khi gọi hàm ngoài context