Extends trong Javascript - kế thừa và ghi đè phương thức
Trong bài này chúng ta sẽ tìm hiểu cách sử dụng từ khóa extends trong Javascript, qua đó sẽ giúp bạn hiểu được tính kế thừa và cách ghi đè phương thức trong js.
Kể từ ES6 thì javascript đã trở thành một ngôn ngữ siêu khó, ta có thể tạo đối tượng bằng nhiều cách khác nhau như function và class. Và với sự ra đời của class thì chúng ta có thêm các khái niệm về tính kế thừa (inheritance) và ghi đè phương thức (overriding).
1. Extends trong Javascript là gì?
Trong javascript, extends là một từ khóa dùng để khai báo một lớp được kế thừa từ một lớp khác. Qua đó lớp kế thừa có thể sử dụng những phương thức và thuộc tính của lớp được kế thừa.
Nếu lớp A kế thừa từ lớp B thì lớp A ta gọi là lớp con, còn lớp B ta gọi là lớp cha.
Bài viết này được đăng tại [free tuts .net]
Câu hỏi được đặt ra là tại sao cần tính kế thừa? Câu trả lời như sau:
Thực tế thì nhiều đối tượng có thể có những thuộc tính và hành động giống nhau, và bắt buộc mỗi đối tượng chúng ta phải cài đặt một class riêng lẻ, thì việc kế thừa sẽ giúp cho việc cài đặt các đối tượng trở nên đơn giản hơn.
Ví dụ: Ta có đối tượng động vật, trong động vật lại có nhiều loại động vật khác nhau như sư tử, rùa.
- Động vật sẽ có các đặc tính như: Số cân, màu lông, chủng loại...
- Động vật sẽ có các hành động như: Ăn, chạy, săn mồi ...
Những đặc tính và hành động trên cũng có trong hai đối tượng sư tử và rùa, vì vậy hai đối tượng này ta sẽ khai báo kế thừa đối tượng động vật.
2. Xây dựng class extends trong javascript
Bây giờ ta sẽ xây dựng ba class như ví dụ mà mình đã nói ở phần 1, bằng cách sử dụng từ khóa extends trong js.
Khai báo lớp Animal.
class Animal { constructor(name) { this.name = name; this.speed = 0; // Tốc độ chạy 0km / h } run(speed) { this.speed = speed; alert(`${this.name} chạy tốc độ là ${this.speed}.`); } stop() { this.speed = 0; alert(`${this.name} đứng yên.`); } }
Khai báo lớp Lion kế thừa từ lớp Animal: Lion có một đặc tính riêng biệt mà động vật khác không có, đó là Gầm. Vì vậy ngoài các hành động run
và stop
thì mình sẽ khai báo thêm hành động roar
.
class Lion extends Animal{ roar(){ alert(`${this.name} đang gầm`); } } let lion = new Lion("Sử tử"); lion.run(80); lion.stop(); lion.roar();
Khai báo lớp con thỏ kế thừa từ lớp Animal: Con thỏ là một động vật nhỏ bé nên nó hay bị các động vật khác tấn công, vì vậy nó có thêm hành động ẩn nấp.
class Rabbit extends Animal { hide() { alert(`${this.name} đang ẩn nấp`); } } let rabbit = new Rabbit("Con thỏ"); rabbit.run(0.05); rabbit.stop(); rabbit.hide();
Như vậy lớp con sẽ có đầy đủ những phương thức và thuộc tính của lớp cha, đây là một quy tắc hiển nhiên trong việc phân cấp cha con.
Có một vấn đề rất quan trọng trong việc kế thừa, đó là mức độ truy cập đến các thuộc tính và phương thức trong lớp. Vấn đề này mình sẽ trình bày trong bài viết private và protected trong js.
3. Overriding trong Javascript
Overriding là kỹ thuật ghi đè hàm (viết lại phương thức) của lớp cha.
Đôi khi những hành động bên trong lớp cha sẽ không thích ứng với các lớp con, lúc này lớp con có thể ghi đè lại các phương thức của lớp cha thông qua kỹ thuật Overriding.
Ví dụ với lớp con thỏ dưới đây, hành động chạy (run) của nó thường sẽ đi kèm với hành động ẩn nấp (hide). Vì vậy ta có thể viết lại hàm run trong lớp Rabbit như sau:
class Animal { constructor(name) { this.name = name; } run() { console.log(`${this.name} đang chạy`); } } class Rabbit extends Animal { // Override hàm run run(){ super.run(); this.hide(); } hide() { console.log(`${this.name} đang ẩn nấp`); } } let rabbit = new Rabbit("Con thỏ"); rabbit.run();
Tòm lại:
- Trong ví dụ này thì phương thức
hide
đã bị ghi đè lại vì nó được khai báo ngay trong lớp con. - Để gọi đến hàm run ở lớp cha thì ta phải sử dụng từ khóa super, nếu không nó sẽ hiểu là bạn đang gọi đếm hàm run ở lớp con.
4. Overriding constructor javascript
Nếu bạn muốn overriding hàm khởi tạo constructor thì cần chú ý một số vấn đề như sau.
- Nếu bạn không muốn override hàm constructor thì không phải làm gì cả.
- Nếu bạn tạo một hàm constructor ở hàm con thì bắt buộc phải sử dụng từ khóa super để gọi đến hàm constructor của lớp cha, và phải đặt vị trí đầu tiên.
Trường hợp này là ok.
class Animal { constructor(name) { console.log('Hàm khởi tạo lớp cha'); } } class Rabbit extends Animal { } let rabbit = new Rabbit();
Trường hợp dưới đây là sai, bởi vì bạn đã tạo một constructor ở lớp con, nên bắt buộc phải sử dụng từ khóa super.
class Animal { constructor(name) { console.log('Hàm khởi tạo lớp cha'); } } class Rabbit extends Animal { constructor(name, age) { console.log('Hàm khởi tạo lớp con'); } }
Sửa lại như sau:
class Animal { constructor(name) { console.log('Hàm khởi tạo lớp cha'); } } class Rabbit extends Animal { constructor(name, age) { super(name); console.log('Hàm khởi tạo lớp con'); } } let rabbit = new Rabbit();
Mặc dù hàm cha không có hàm constructor nhưng bạn cũng phải sử dụng từ khóa supper.
class Animal { } class Rabbit extends Animal { constructor() { super(); console.log('Hàm khởi tạo lớp con'); } } let rabbit = new Rabbit();
Như vậy là mình đã giới thiệu xong từ khóa extends trong javascript, cũng như cách sử dụng nó để tạo ra những lớp kế thừa. Bài này mình xin dừng ở đây, hẹn gặp lại các bạn ở bài tiếp theo.