Hi 🤓 Cảm ơn bạn đã ghé thăm blog này, nếu những bài viết trên blog giúp ích cho bạn. Bạn có thể giúp blog hiển thị quảng cáo bằng cách tạm ngừng ad blocker 😫 và để giúp blog duy trì hoạt động nếu bạn muốn.
Cảm ơn bạn!

Chào các bạn, ở bài viết trước chúng ta đã tìm hiểu về class trong JavaScript, hôm nay chúng ta sẽ tìm hiểu về kế thừa trong JavaScript nhé!

Class Inheritance

Kế thừa các bạn có thể hiểu là nó cho phép chúng ta tạo một class, và class này có tất cả các chức năng từ một class cha (class mới thừa kế những chức năng từ class cha). Ngoài ra, kế thừa còn cho phép chúng ta thêm các chức năng mới.

👉 Các bạn có thể đọc về Prototype trong JavaScript để hiểu hơn về kế thừa trong JavaScript nhé.

Khi chúng ta sử dụng class inheritance, một class có thể kế thừa tất cả các methods và properties của một class khác.

Kế thừa là một tính năng rất hay cho phép chúng ta tái sử dụng các đoạn code.

Để sử dụng kế thừa của class, bạn sử dụng từ khóa extends.

Ví dụ:

// parent class
class Person { 
    constructor(name) {
        this.name = name;
        this.blogName = 'homiedev.com'
    }

    greet() {
        console.log(`Hello I'm ${this.name}`);
    }
}

// class Student kế thừa các method, property từ Person
class Student extends Person {

}

let student1 = new Student('Trang');
student1.greet();

Ở ví dụ trên, class Student kế thừa tất cả các methods và properties của class Person. Do đó, Student bây giờ sẽ có thuộc tính name và method greet().

Kết quả chúng ta được:

Hello I'm Trang

Từ khóa super trong JavaScript và một số cách sử dụng

Từ khóa super trong JavaScript được sử dụng để truy cập các properties của một object literal (sử dụng ký hiệu { }) hoặc [[Prototype]] của class. Nó còn dùng để invoke (thực thi) hàm constructor của parent class (lớp cha).

Syntax:

super([arguments]) // thực thi parent constructor
super.propertyOnParent
super[expression]

Chúng ta cùng xem qua một vài ví dụ về cách sử dụng từ khóa super.

class Animal {
  constructor(name) {
    this.name = name;

    console.log("Animal's constructor");
  }

  sleep() {
    console.log(`${this.name} is sleeping 💤.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // thực thi parent constructor - Animal constructor và truyền giá trị cho name

    this.eat = '🍗';
  }
}

const d = new Dog("BLAZE");
d.sleep(); // BLAZE is sleeping 💤.

Ở ví dụ trên, vì mình muốn tự tạo một constructor cho class Dog và muốn thêm thuộc tính eat vào constructor này. Để thêm, sửa thuộc tính thì chúng ta sẽ sử dụng từ khóa this như trên.

Khi sử dụng từ khóa super như trên, nó sẽ thực thi hàm constructor của Animal như mình đã nói, cùng xem hình bên dưới nhé:

call super js

Vấn đề là tại sao chúng ta cần sử dụng từ khóa super trong constructor của class Dog? để hiểu thì bây giờ mình thử xóa dòng super(name) đi thử nhé. Kết quả chúng ta sẽ nhận được là:

class Dog extends Animal {
  constructor(name) {
    // super(name);

    this.eat = "🍗";
  }
}

const d = new Dog("BLAZE");
// ❌ Lỗi: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

d.sleep(); // BLAZE is sleeping 💤.

Lỗi trên xảy ra khi chúng ta sử dụng this trước khi gọi super() trong lớp con - Dog class.

Nguyên nhân là vì khi chúng ta tạo một constructor trong lớp con (derived class), từ khóa this trong hàm constructor vẫn chưa được xác định hay chưa được khởi tạo. Vì vậy chúng ta cần xác định this trong constructor này là cái gì 😃, để làm được như vậy thì các bạn phải sử dụng từ khóa super, nó sẽ thực thi hàm constructor của parent class (Animal) và từ đó this trong constructor của Dog class sẽ có các thuộc tính của lớp cha. Sau đó, các bạn có thể truy cập và chỉnh sửa các thuộc tính của this.

Nếu chúng ta không định nghĩa một constructor cho class thì JavaScript sẽ tự tạo một default constructor:

  1. Nếu class là parent class (base class), default constructor sẽ trống.
constructor() {}
  1. Nếu class là derived class, default constructor sẽ gọi parent constructor:
constructor(...args) {
  super(...args);
}

👉 Đọc thêm: Spread Operator Javascript là gì?.

Khi parent class có method hoặc static field, chúng ta có thể truy cập chúng theo cách sau:

class Base {
  static baseStaticField = 'homiedev.com';
  baseMethod() {
    return '🎉';
  }
  author = 'Nguyen Anh Vu';
}

class Extended extends Base {
  extendedField = super.baseMethod(); // 🎉
  static extendedStaticField = super.baseStaticField; // 'homiedev.com'
  name = super.author; // undefined
}

Chúng ta có thể truy cập các static field và method như trên, vì từ khóa super trong trường hợp này sẽ tìm các thuộc tính đó trong Base.prototype. Còn với field name sẽ nhận giá trị undefined, vì author là một thuộc tính của Base instance (một instance được tạo ra khi sử dụng từ khóa new), nên chúng ta không thể sử dụng super để truy xuất nó.

Cú pháp super.propsuper[expr] dùng được trong trường hợp chúng ta đang khởi tạo field, còn super(...arg) hợp lệ khi được dùng trong hàm constructor của class.

Trong ví dụ này, super được sử dụng với object literals:

const objA = {
  methodA() {
    console.log('method A');
  }
}

const objB = {
  methodB() {
    super.methodA();
  }
}

Object.setPrototypeOf(objB, objA);
objB.methodB(); // "method A"

Trong ví dụ trên, chúng ta định nghĩa hai method ở hai object khác nhau, trong methodB ta sử dụng từ khóa super để gọi methodA, cách này hoạt động vì super sẽ tìm methodA ở prototype của objB, mà prototype của objBobjA đã set thông qua method Object.setPrototypeOf().


Trên đây là bài viết về kế thừa trong JavaScript sử dụng class, chúng ta cũng đã tìm hiểu một số cách sử dụng của từ khóa super. Hi vọng bài viết giúp ích cho các bạn. Cảm ơn các bạn đã đọc bài viết.

Nếu chưa rõ về nội dung ở trên, chúng ta cùng thảo luận ở phần bình luận nhé 😃.

Có thể bạn thích ⚡
homiedev
About Me

Hi, I'm @devnav. Một người thích chia sẻ kiến thức, đặc biệt là về Frontend 🚀. Trang web này được tạo ra nhằm giúp các bạn học Frontend hiệu quả hơn 🎉😄.

Chúc các bạn tìm được kiến thức hữu ích trong blog này 😁😁.