Tổng quan các khái niệm
Phương thức ràng buộc
// Ràng buộc cho đối tượng
class Learner {
constructor(fullName, yearsOld, sex) {
this.fullName = fullName;
this.yearsOld = yearsOld;
this.sex = sex;
}
selectSubject() {
// nội dung phương thức
}
}
const studentInstance = new Learner('Minh', 20, 'nam');
// Khi gọi phương thức từ lớp, mặc định ràng buộc với đối tượng
studentInstance.selectSubject(); // tương đương studentInstance.selectSubject(studentInstance)
// Ràng buộc cho lớp
class Learner {
constructor(fullName, yearsOld, sex) {
this.fullName = fullName;
this.yearsOld = yearsOld;
this.sex = sex;
}
static selectSubject() { // sử dụng decorator để ràng buộc cho lớp
// tham số đầu tiên là tên lớp
}
accessBoth() {
console.log(this); // đối tượng hiện tại
console.log(this.constructor); // lớp gốc
}
}
// Gọi phương thức thông qua lớp
Learner.selectSubject();
Phương thức không ràng buộc
// Không ràng buộc cho lớp hay đối tượng - phương thức tĩnh
class Learner {
static methodName() {
// phương thức tĩnh không tự động truyền tham số khi gọi
}
}
Thuộc tính ẩn
// Cách ẩn thuộc tính với tiền tố __
/*
1. Thêm __ vào trước tên thuộc tính hoặc phương thức
2. Ẩn bên ngoài nhưng vẫn truy cập được bên trong, cần tạo giao diện truy cập bên trong lớp
3. Trong quá trình định nghĩa lớp sẽ có sự biến đổi cú pháp, sau đó __ không còn biến đổi nữa
*/
Decorator property
// Chuyển phương thức thành thuộc tính ảo
class Example {
get propertyName() {
return this._value;
}
set propertyName(value) {
this._value = value;
}
}
// Cách khác
const propertyName = function(getter, setter, deleter);
Ba đặc điểm chính của hướng đối tượng
Đóng gói
Đóng gói giống như hàm và lớp, giúp đóng gói mã nguồn dạng script thành cấu trúc rõ ràng.
Kế thừa (Quan trọng)
- Khái niệm: Kế thừa là cách tạo lớp mới, lớp mới gọi là lớp con hoặc lớp dẫn xuất, lớp được kế thừa gọi là lớp cha hoặc lớp cơ sở
- Mục đích: Lớp giải quyết vấn đề lặp code giữa các đối tượng, kế thừa giải quyết vấn đề lặp code giữa các lớp
- Cách sử dụng:
- Lớp mới: kế thừa từ lớp object và tất cả lớp con cháu
- Lớp cổ điển: không kế thừa từ lớp object
Cách sử dụng kế thừa
class BaseClass1 { // mặc định kế thừa lớp hệ thống(object)
constructor() {}
}
class BaseClass2 {
constructor() {}
}
class DerivedClass1 extends BaseClass1 {
constructor() {
super();
}
}
class DerivedClass2 extends BaseClass1 {
constructor() {
super();
}
}
// Kiểm tra lớp cha mà lớp con kế thừa
console.log(DerivedClass1.__proto__); // xem lớp cha
console.log(BaseClass1.__proto__); // object
Ví dụ chọn môn học
// Lớp cha (lớp chung)
class Human {
static institution = 'Học viện ABC';
constructor(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
}
// Lớp sinh viên
class Student extends Human {
constructor(name, age, gender, subjects = []) {
super(name, age, gender);
this.subjectList = subjects;
}
enrollSubject(subject) {
this.subjectList.push(subject);
console.log(`${this.name} đăng ký thành công môn ${this.subjectList}`);
}
}
// Lớp giáo viên
class Instructor extends Human {
constructor(name, age, gender, rank) {
super(name, age, gender);
this.rank = rank;
}
assignGrade(studentObj, grade) {
studentObj.grade = grade;
console.log(`${this.name} đã chấm điểm ${grade} cho ${studentObj.name}`);
}
}
const student = new Student(); // tạo đối tượng sinh viên
const instructor = new Instructor(); // tạo đối tượng giáo viên
Thứ tự tìm kiếm thuộc tính trong kế thừa
// Trong kế thừa đơn
// Thứ tự: đối tượng >> lớp tạo đối tượng >> lớp cha kế thừa
// Bài tập 1
class ParentClass {
methodOne() {
console.log('ParentClass.methodOne');
}
methodTwo() {
console.log('ParentClass.methodTwo');
this.methodOne();
}
}
class ChildClass extends ParentClass {
methodOne() {
console.log('ChildClass.methodOne');
}
}
const instance = new ChildClass();
instance.methodTwo();
// Bài tập 2 - tương tự như trên
super() và danh sách mro()
// super() sử dụng theo thứ tự mro()
class Person {
static institution = 'Học viện';
constructor(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
}
class Educator extends Person {
constructor(name, age, gender, experienceLevel) {
super(name, age, gender);
this.experienceLevel = experienceLevel;
}
}
// Bài tập danh sách mro()
class First {
checkMethod() {
console.log('từ First');
super.checkMethod();
}
}
class Second {
checkMethod() {
console.log('từ Second');
}
}
class Third extends First {
constructor() {
super();
}
}
const obj = new Third();
obj.checkMethod(); // tìm kiếm theo danh sách mro
Đa hình và tính đa hình
// Lớp trừu tượng chỉ dùng để kế thừa, không thể khởi tạo
const { Abstract } = require('abstract-class');
class AnimalBase {
speak() {
throw new Error('Phải triển khai phương thức speak!');
}
authenticate() {
throw new Error('Phải triển khai phương thức authenticate!');
}
}
class Human extends AnimalBase {
speak() {
console.log('Nói chuyện');
}
authenticate() {
console.log('Xác thực người dùng');
}
}
class Pig extends AnimalBase {
speak() {
console.log('Oc oc');
}
}
class Dog extends AnimalBase {
speak() {
console.log('Gâu gâu');
}
}
// Đặc điểm của đa hình: gọi phương thức mà không cần quan tâm kiểu dữ liệu
function makeSound(entity) {
return entity.speak();
}
makeSound(new Human());
makeSound(new Pig());
makeSound(new Dog());
// Lớp cha giới hạn hành vi lớp con
class BaseAnimal {
speak() {
throw new Error('Không tìm thấy phương thức speak!');
}
}