Chúng ta đã biết, mọi thứ trong JS đều là object và do đó mà JS là một ngôn ngữ Object Oriented Programming.

Để có thể dùng đi dùng lại một “blueprint” của nhóm các đối tượng thì ta cần sử dụng class, một tính năng xuất hiện trong JS ở phiên bản ES6.

Class Declaration

Tên của lớp đối tượng sử dụng CamelCase.

class Person {
  name
 
  // Default value
  age = 20
 
  // Private property
  #isMarried = false
 
  // Method
  getName() {
    return this.name
  }
}

Một sự khác biệt của function và class là tính Hoisting (đưa lên đầu). Trong khi hàm có thể gọi trước khi được định nghĩa thì lớp đối tượng cần phải định nghĩa trước khi được gọi.

const p = new Rectangle() // ReferenceError
 
class Rectangle {}

Ngoài ra, có thể sử dụng cách khác, gọi là Class Expression để khai báo lớp đối tượng.

Class Instantiation

Để khởi tạo một đối tượng từ lớp đối tượng, ta sử dụng từ khóa new:

class Person {
  name
 
  // Default value
  age = 20
 
  // Private property
  #isMarried = false
}
 
const person = new Person()
 
console.log(person) // Person {name: undefined, age: 20}

Class Constructor

Ta có thể thiết lập một constructor để vừa thêm thuộc tính vừa khởi tạo giá trị của chúng.

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}
 
const person = new Person("Quân", 20)
 
console.log(person) // Person {name: 'Quân', age: 20}

Info

  • Cũng có thể thiết lập default constructor cho lớp đối tượng.
  • Tuy nhiên, một class chỉ có thể có một constructor duy nhất do JS không có overloading trong cùng một class.

Prototype

Để thêm một thuộc tính hoặc phương thức vào lớp đối tượng sau khi lớp đối tượng được tạo ra thì ta sử dụng từ khóa prototype.

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}
 
Person.prototype.hair = "curly"
Person.prototype.getAge = function () {
  return this.age
}
 
const person = new Person("Quân", 20)
 
console.log(person.hair) // "curly"
console.log(person.getAge()) // 20

Important

Cần phân biệt với việc thêm thuộc tính hoặc phương thức vào đối tượng, khi đó chúng ta chỉ cần sử dụng toán tử .

Class Methods

Phương thức của lớp đối tượng trong JS cũng có nguyên lý hoạt động tương tự C-C++:

class Person {
  constructor(firstName, lastName, age) {
    this.firstName = firstName
    this.lastName = lastName
    this.age = age
  }
 
  // Do not use `funtion` keyword or `=>` syntax
  getFullName() {
    const fullName = this.firstName + " " + this.lastName
    return fullName
  }
}
 
const person = new Person("Quân", "Lê Minh", 25)
 
console.log(person.getFullName()) // Quân Lê Minh

Tip

Muốn khai báo phương thức tĩnh (Static) thì đặt từ khóa static trước tên phương thức.

Summary

Tóm lại:

  • Thêm vào đối tượng: toán tử ..
  • Thêm vào lớp đối tượng: từ khóa prototype.
  • Gọi từ đối tượng: toán tử . hoặc toán tử [] nếu là thuộc tính.
  • Gọi từ lớp đối tượng: có từ khóa static, sử dụng tên lớp đối tượng.

OOP in JS

Inheritance

Để kế thừa từ một lớp cơ sở, sử dụng từ khóa extends, thay vì dùng access modifier (::) như C++.

class Student extends Person {
  saySomething() {
    console.log("I am a child of the person class")
  }
}
 
// Using parent class's constructor
const person = new Student("Quân", "Lê Minh", 20)
 
console.log(person.firstName) // Quân
 
person.saySomething() // I am a child of the person class

Polymorphism

Chúng ta có thể overriding các hàm của lớp cơ sở bên trong lớp dẫn xuất. Chẳng hạn, có thể tạo một constructor riêng cho lớp dẫn xuất thay vì gọi constructor của lớp cơ sở rồi thêm các thuộc tính và phương thức mà chúng ta cần.

Từ khóa super dùng để truy cập và gọi phương thức của lớp cơ sở bên trong phương thức của lớp dẫn xuất. Nếu super được sử dụng trong constructor của lớp dẫn xuất, nó cần phải đi một mình và đứng trước các câu lệnh sử dụng this.

class Student extends Person {
  constructor(firstName, lastName, age, gender) {
    super(firstName, lastName, age) // call parent constructor
    this.gender = gender
  }
}
 
const person = new Student("Quân", "Lê Minh", 20, 1)
 
console.log(person.age) // 20

Đoạn code trên customize một constructor riêng cho lớp dẫn xuất. Nếu ở lớp dẫn xuất có một phương thức trùng tên với lớp cơ sở, tính đa hình (Polymorphism) sẽ nhập cuộc. Nói cách khác, phương thức ở lớp dẫn xuất sẽ ghi đè lên phương thức của lớp cơ sở.

Info

Khi gọi một hàm của lớp cơ sở, không nhất thiết phải truyền hết tất cả các tham số.

class Student extends Person {
  constructor(firstName, lastName, age, gender) {
    super(firstName, age) // call parent constructor without lastName property
    this.lastName = ""
    this.gender = gender
  }
}