Hôm nay mình sẽ giới thiệu với bạn về class trong JavaScript. Class được tạo ra như thế nào? Sử dụng class ra sao. Hãy theo dõi nhé! Show
1. Class là gì?Class có thể hiểu là một khuôn mẫu. Trong lập trình hướng đối tượng class (hay lớp) được sử dụng để tạo ra các đối tượng (mà mình đã giới thiệu ở bài đối tượng trong JavaScript). Đối tượng thì sẽ có các thuộc tính, phương thức. Các đối tượng giống nhau sẽ có những thuộc tính và phương thức giống nhau. Từ phiên bản ECMAScript 6 thì JavaSript hỗ trợ tạo ra class giống các ngôn ngữ lập trình hướng đối tượng khác (Java, PHP, Python...). Tham khảo:
Có thể xem như việc sử dụng class và object sẽ giúp chúng ta có cái nhìn chuẩn hơn về code và để những bạn mới tiếp cận lập trình JavaSript cũng thấy gần gũi hơn. Cú pháp để tạo ra một class trong JavaSript: // Hàm khởi tạo constructor() { ... } // Các phương thức phuongThuc1() { ... } phuongThuc2() { ... } phuongThuc3() { ... } } } Đây là ví dụ khi khai báo Object mà mình đã giới thiệu ở bài trước: // Tạo các thuộc tính hoTen: 'Ngô Minh Trung', gioiTinh: 'Nam', MSSV: 'B1704863', namSinh: '1999', // Tạo các phương thức hocBai: function() { console.log('Đang học bài...'); }, diNgu: function() { console.log('Đang ngủ...'); }, xemPhim: function() { console.log('Đang xem phim...'); } } Nhưng ở đây, ta chỉ tạo được một đối tượng cụ thể. Nếu muốn tạo ra đối tượng khác thì ta lại phải viết code lại gần như từ đầu. Nếu các đối tượng sinh viên tạo ra cũng có các thuộc tính giống nhau như: Và các thuộc tính chung như Thì có nghĩa là code của bạn chưa tối ưu. Code thừa rất nhiều. Để tránh vấn đề này, chúng ta có class: // Tạo ra một class SinhVien class SinhVien { //Hàm khởi tạo constructor(hoTen, gioiTinh, MSSV, namSinh) { this.hoTen = hoTen; this.gioiTinh = gioiTinh; this.namSinh = namSinh; this.MSSV = MSSV; } //Khai báo phương thức hocBai() { console.log('Đang học bài...'); } diNgu() { console.log('Đang ngủ...'); } xemPhim() { console.log('Đang xem phim...'); } } Đây có thể hiểu như là một khuôn mẫu chung cho các đối tượng sinh viên. Khi muốn tạo ra đối tượng cụ thể thì ta sẽ sử dụng class // Tạo đối tượng từ class SinhVien var sinhVien1 = new SinhVien(); var sinhVien2 = new SinhVien(); var sinhVien3 = new SinhVien(); > Lưu ý: Theo quy tắc thì bạn nên đặt tên class là viết HOA chữ cái đầu tiên như 2. Constructor là gì?Phương thức khởi tạo (constructor hay là hàm khởi tạo) là một phương thức đặc biệt để tạo và khởi tạo một đối tượng từ một class nào đó. Nó sẽ tự động được gọi khi bạn tạo ra một đối tượng từ class. Chỉ có thể có một phương thức đặc biệt với tên là “constructor” trong một lớp. Một lỗi Tức là JavaScript không hỗ trợ có nhiều constructor như các ngôn ngữ khác. Một ví dụ về constructor: constructor(hoTen, gioiTinh, MSSV, namSinh) { this.hoTen = hoTen; this.gioiTinh = gioiTinh; this.namSinh = namSinh; this.MSSV = MSSV; } Như vậy để tạo hàm constructor thì chỉ cần dùng từ khóa Sau đó
gán các thuộc tính của lớp bằng từ khóa Bạn nhìn như thế này sẽ dễ hiểu hơn: constructor(a, b, c, d) { this.hoTen = a; this.gioiTinh = b; this.namSinh = c; this.MSSV = d; } Sau đó khi khởi tạo đối tượng thì ta sẽ truyền đối số như thế này: var sinhVien1 = new SinhVien(a, b, c, d); Tuy nhiên, để sau này dễ dàng biết thuộc tính nào kết nối với tham số nào thì chúng ta nên sử dụng tên tham số và đối số trong hàm constructor giống nhau. constructor(hoTen, gioiTinh, MSSV, namSinh) { this.hoTen = hoTen; this.gioiTinh = gioiTinh; this.namSinh = namSinh; this.MSSV = MSSV; } > Ghi chú: Khi tạo class thì ta gọi là tham số. Khi khởi tạo và truyền dữ liệu thì ta gọi nó là đối số Chúng ta có nhiều cách gọi hàm như trong bài hàm mình có đề cập đến. Ví dụ như sau: // Khởi tạo các đối tượng sinh viên var sinhVien1 = new SinhVien("Ngô Minh Trung", "Nam", "B1704863", "1999"); var sinhVien2 = new SinhVien("Nguyễn Minh Đức", "Nam"); var sinhVien3 = new SinhVien(); // Log ra thông tin đối tượng console.log(sinhVien1); console.log(sinhVien2); console.log(sinhVien3); Kết quả ta được như sau:
> Lưu ý: Kể cả khi bạn không định nghĩa rõ ràng một constructor. JavaScript cũng sẽ tự động thêm vào một constructor rỗng
3. Phương thức getter và setter trong JavaScriptCác phương thức getter và setter được sử dụng để truy cập / sửa đổi thuộc tính trong class. Có thể hiểu là một loại hàm để lấy dữ liệu và một loại hàm dùng đế gán dữ liệu. 3.1. Phương thức getterCác phương thức getter được sử dụng để truy cập các thuộc tính của một đối tượng. Để
tạo phương thức getter trong JavaScript, chúng ta sử dụng từ khóa Ví dụ: // Các phương thức getter get getHoTen() { return this.hoTen; } get getGioiTinh() { return this.gioiTinh; } get getNamSinh() { return this.hoTen; } get getMSSV() { return this.MSSV; } Có bao nhiêu thuộc tính thì sẽ có bấy nhiêu hàm getter trong class. Khi chúng ta sử dụng, chúng ta có thể truy cập giá trị như là một thuộc tính. Ví dụ: var sinhVien1 = new SinhVien("Ngô Minh Trung", "Nam", "B1704863", "1999"); // Truy cập giới tính thông qua phương thức getter console.log(sinhVien1.getGioiTinh); Ngay cả khi getter là một phương thức, nếu bạn cố gắng truy cập giá trị dưới dạng một phương thức (sử dụng ngoặc tròn console.log(sinhVien1.getGioiTinh()); Thì sẽ bị lỗi: 3.2. Phương thức setterPhương thức setter cho phép bạn thay đổi giá trị của thuộc tính của đối tượng. Để tạo ra phương thức setter ta sử dụng từ khóa Ví dụ: this.hoTen = hoTen; } set setGioiTinh(gioiTinh) { this.gioiTinh = gioiTinh; } set setNamSinh(namSinh) { this.namSinh = namSinh; } set setMSSV(MSSV) { this.MSSV = MSSV; } Cũng tương tự getter, setter cũng vậy có bao nhiêu thuộc tính thì chúng ta tạo bấy nhiêu hàm setter. Để sử dụng phương thức setter bạn phải truyền vào đối số tương ứng. var sinhVien1 = new SinhVien("Ngô Minh Trung", "Nam", "B1704863", "1999"); // Thiết lập lại giới tính sinhVien1.setGioiTinh = "Nữ"; // Log ra giới tính hiện tại console.log(sinhVien1.getGioiTinh); Hiện tại, giới tính đã đổi từ > Lưu ý #1: JavaScript không hỗ trợ đầy đủ tính đóng gói trong Lập trình hướng đối tượng như Java, PHP hay Python. Vì thế, mọi thuộc tính đều có thể truy cập nếu bạn biết tên của thuộc tính đó (Giống như phạm vi truy cập public trong Java, tham khảo trong hướng dẫn: HỌC LẬP TRÌNH JAVA). > Lưu ý #2: JavaScript nguyên bản thì không hỗ trợ private, protected nhưng bạn có thể mô phỏng chúng (Tuy nhiên sẽ hơi rắc rối đó) 4. Phương thức tĩnh (Static method)Từ khóa Các phương thức tĩnh được gọi mà không cần khởi tạo lớp của chúng và không thể được gọi thông qua một đối tượng của lớp. Phương thức tĩnh thường được sử dụng để tạo các chức năng tiện ích cho một ứng dụng. Ví dụ: constructor(x, y) { this.x = x; this.y = y; } static distance(a, b) { const dx = a.x - b.x; const dy = a.y - b.y; return Math.hypot(dx, dy); } } const p1 = new Point(5, 5); const p2 = new Point(10, 10); p1.distance; //undefined p2.distance; //undefined console.log(Point.distance(p1, p2)); // 7.0710678118654755 Chúng ta
không thể gọi hàm distance thông qua 5. Hoisting đối với classKhông giống như các hàm và các khai báo JavaScript khác, các khai báo class (lớp) không được hoisting. var sinhVien1 = new SinhVien("Ngô Minh Trung"); class SinhVien { constructor(name) { this.hoTen = hoTen; } } Ví dụ trên sẽ tạo ra lỗi. Để tránh lỗi, bạn phải khai báo một lớp trước khi bạn có thể sử dụng nó, như vậy: constructor(name) { this.hoTen = hoTen; } } var sinhVien1 = new SinhVien("Ngô Minh Trung"); 6. Kế thừa classJavaScript cũng hỗ trợ tính kế thừa (inheritance) như các ngôn ngữ khác, để tạo một lớp kế thừa, bạn sử dụng từ khóa class SinhVien extends ConNguoi {...} Kế thừa cho chúng ta khả năng sử dụng lại code đã viết. Sử dụng lại các thuộc tính và phương thức của một lớp có sẵn khi tạo một lớp mới giúp chúng ta tiết kiệm thời gian làm việc. Một lớp được kế thừa từ lớp cha sẽ có tất cả tác phương thức từ lớp cha. Ví dụ: Ta có một lớp constructor(hoTen, namSinh) { this.hoTen = hoTen || "unknown"; this.namSinh = namSinh || "unknown"; } getHoTen() { return "Tên " + this.hoTen + ", sinh năm " + this.namSinh; } } class SinhVien extends ConNguoi { constructor(maSSV, hoTen, namSinh) { super(hoTen, namSinh); this.maSSV = maSSV || "unknown"; } getThongTin() { return this.getHoTen() + ", mã sinh viên " + this.maSSV; } } var sinhVien1 = new SinhVien("B1704863", "Ngô Minh Trung", "1999"); console.log(sinhVien1.getThongTin()); Kết quả: Tên Ngô Minh Trung, sinh năm 1999, mã sinh viên B1704863 Qua đoạn code trên ta thấy:
Ví dụ: constructor(hoTen, namSinh) { this.hoTen = hoTen || "unknown"; this.namSinh = namSinh || "unknown"; } getThongTin() { return "tên " + this.hoTen + ", sinh năm " + this.namSinh; } } class SinhVien extends ConNguoi { constructor(maSSV, hoTen, namSinh) { super(hoTen, namSinh); this.maSSV = maSSV || "unknown"; } // Ghi đè phương thức getThongTin() { return ( "MSSV " + this.maSSV + ", " + super.getThongTin() ); } } var sinhVien1 = new SinhVien("B1704863", "Ngô Minh Trung", "1999"); console.log(sinhVien1.getThongTin()); Kết quả được như sau: MSSV B1704863, tên Ngô Minh Trung, sinh năm 1999 Như vậy, ở class 7. Từ khóa this trong classVề từ khóa
Từ khóa constructor(hoTen, namSinh) { this.hoTen = hoTen || "unknown"; this.namSinh = namSinh || "unknown"; } getThongTin() { return "Tên " + this.hoTen + ", sinh năm " + this.namSinh; } } Với class này mình sử dụng từ khóa Khi đoạn code được biên dịch thì hàm 8. Kiểm tra loại đối tượng với instanceof
Để mình ví dụ: constructor(hoTen, namSinh) { this.hoTen = hoTen || "unknown"; this.namSinh = namSinh || "unknown"; } getThongTin() { return "Tên " + this.hoTen + ", sinh năm " + this.namSinh; } } var sinhVien1 = new ConNguoi("Ngô Minh Trung", "1999"); var sinhVien2 = {}; console.log(sinhVien1 instanceof ConNguoi); // true console.log(sinhVien2 instanceof ConNguoi); // False Như vậy ta sẽ thấy Còn đối với Ví dụ: constructor(hoTen, namSinh) { this.hoTen = hoTen || "unknown"; this.namSinh = namSinh || "unknown"; } getHoTen() { return "Tên " + this.hoTen + ", sinh năm " + this.namSinh; } } class SinhVien extends ConNguoi { constructor(maSSV, hoTen, namSinh) { super(hoTen, namSinh); this.maSSV = maSSV || "unknown"; } getThongTin() { return this.getHoTen() + ", mã sinh viên " + this.maSSV; } } var sinhVien1 = new SinhVien("B1704863", "Ngô Minh Trung", "1999"); console.log(sinhVien1 instanceof SinhVien); // true console.log(sinhVien1 instanceof ConNguoi); // true Kết quả cả hai đều trả về 9. Class và prototypesCú pháp lớp (class) trong JavaScript thực hiện trừu tượng hóa các nguyên mẫu (prototypes) một cách tuyệt vời. Các lớp được xây dựng từ việc kế thừa các nguyên mẫu. Mọi lớp đều được xem như là một hàm và tạo ra một thể hiện khi được gọi như một hàm tạo. Chúng ta hãy so sánh hai đoạn mã sau nhé. Đoạn code 1 sử dụng từ khóa class: constructor(hoTen, namSinh) { this.hoTen = hoTen || "unknown"; this.namSinh = namSinh || "unknown"; } getThongTin() { return "Tên " + this.hoTen + ", sinh năm " + this.namSinh; } } var sinhVien1 = new ConNguoi("Ngô Minh Trung", 1999); console.log(sinhVien1.getThongTin()); console.log(sinhVien1 instanceof ConNguoi); Đoạn code thứ 2 sử dụng prototype (tương tự như tạo object ở bài trước) function ConNguoi(hoTen, namSinh) { this.hoTen = hoTen || "unknown"; this.namSinh = namSinh || "unknown"; } ConNguoi.prototype.getThongTin = function() { return "Tên " + this.hoTen + ", năm sinh" + this.namSinh; } var sinhVien1 = new ConNguoi("Ngô Minh Trung", 1999); console.log(sinhVien1.getThongTin()); console.log(sinhVien1 instanceof ConNguoi); Đây là hai đoạn code tương đương với nhau. Đối với từ khóa class sẽ dễ sử dụng hơn nếu bạn đã quen với cách lập trình của ngôn ngữ Java hoặc C#. Nếu bạn thích sử dụng từ khóa class hơn thì các bạn cũng nên hiểu rõ về kế thừa prototype nhé. Vì trong JavaScript prototype rất quan trọng. Tổng kếtMÌnh vừa giới thiệu các bạn về class trong JavaSript đây chính là một điểm mới tránh các rườm rà của Object trước đây, class tường minh hơn sẽ dễ tiếp cận hơn. Ngoài ra, mình cũng đã giới thiệu các bạn về một số khái niệm khác liên quan đến class trong JavaScript. Đây chỉ mới là một chút nền tảng cho các bạn học những thư viện hay framework của JavaScript về sau. Hẹn gặp lại bạn ở bài học tiếp theo. |