Lập trình hướng đối tượng trong javascript

Lập trình hướng đối tượng trong javascript hướng dẫn bạn các cách tạo đối tượng trong javavascript và các vấn đề trong OPP.

A. Khái niệm hướng đối tượng

Javascript được thiết kế dựa trên mô hình đối tượng đơn giản. Mỗi đối tượng chứa nhiều thuộc tính.

Thuộc tính có thể là 1 biến hoặc 1 hàm, hàm bên trong đối tượng còn được gọi là phương thức.

Có những đối tượng có sẵn trong javascript như Date, Number, String, Boolean, Array, Math, RegExp… Và bạn cũng có thể tạo ra các đối tượng mới để sử dụng .

Lập trình hướng đối tượng trong javascript tức là xử lý các yêu cầu bằng cách đối tượng hóa các dữ liệu cần xử lý.

Có các cách để bạn tạo đối tượng trong javascript là Object literals và Object constructor functions.

B. Sử dụng Ojbject Literals

Tạo đối tượng với Object literals là cách tạo đơn giản , gọn, nhanh. Đối tượng được biểu diễn bằng cặp dấu {} , bên trong là các cặp name-value, các cặp này cách nhau bởi dấu phẩy. Ví dụ :

<script>
var url = {
    protocol: "https",
    domain: "longnv.name.vn",
    full: function() { return this.protocol + "://" + this.domain }
}
</script>

Việc dùng đối tượng theo kiểu object literals thế này rất dễ. Vì nó giúp gom các giá trị và hàm lại với nhau chung trong 1 biến. Code nhờ đó dễ đọc hơn. Sau đây là 1 ví dụ khác:

<script>
sv1 = {
    ho: "Nguyễn văn",
    ten: "Tèo",
    hoten: function() { return this.ho + " " + this.ten},
    emails: ["meomeo@gomeo.com",  "teonv@gmail.com" ]
}
</script>

Hiển thị giá trị thuộc tính/gọi phương thức

<script>
console.log(sv1.ten); //Tèo
console.log(sv1.hoten()); //Nguyễn văn Tèo
console.log(sv1.emails[0]); //meomeo@gomeo.com
</script>

Thay đổi giá trị của thuộc tính

<script>
sv1.ho ="Phan Thị";
sv1.ten="Lượm";
sv1.emails[1] ="huhu@giahu.com";

console.log(sv1.hoten()); //Phan Thị Lượm
console.log(sv1.emails[1]); //huhu@giahu.com
</script>

Bên cạnh việc tạo object bằng cặp { } thì bạn có thể tạo bằng lệnh new Object().

<script>
var ptb2 = new Object();
ptb2.a = 1;
ptb2.b = 5;    
ptb2.c = -4; 
ptb2.nhap = function(){
    this.a = prompt("Nhập a");
    this.b = prompt("Nhập b");
    this.c = prompt("Nhập c");
};
ptb2.tinhdelta = function(){
    return this.b*this.b - 4*this.a*this.c;
};
ptb2.xem = function() {
    console.log(this.a, this.b, this.c); 
    console.log(this.tinhdelta());
}
</script>

<script>
    ptb2.xem(); 
</script>

Bạn có thể tạo đối tượng như trên ở bất kỳ đâu. Tuy nhiên cách này cũng có những hạn chế. Hạn chế thứ nhất là không hỗ trợ thực thể hóa đối tượng (instantiation), hạn chế 2 là không có tính chất kế thừa (inheritance)


Mời bạn thực hiện: Tạo 1 đối tượng sinh viên bằng cách dùng object literals. Bao gồm các thuộc tính/phương thức : ma_sinh_vien, ho_va_ten, lop, ngay_nhap_hoc, diem_trung_binh, tinhHocLuc(), thoiGianRaTruong()

  • Phương thức tinhHocLuc() trả về học lực của sinh viên theo thang điểm: < 5: Học lực Yếu, Từ 5 đến < 7 điểm: Học lực Trung bình, Từ 7 đến 8: Học lực Khá, Từ 8 đến 10: Học lực Giỏi
  • Phương thức thoiGianRaTruong() trả về số tháng còn lại theo dự kiến tổng thời gian học là 28 tháng. Ví dụ nhập học ngày 04/01/2019 thì dự kiến sẽ còn 17 tháng nữa sẽ ra trường.

C. Tạo đối tượng với Object contructor functions

Object constructor functions hay tạo khuôn mẫu cho đối tượng bằng cách sử dụng hàm (khởi tạo). Cách này có ưu điểm sau:

  • Có thể thực thể hóa các đối tượng (instantiation)
  • Có thể được kế thừa hoặc cho phép kế thừa (inherited)

Tạo đối tượng dùng cách Object constructor functions

<script>
function Khachhang(ht, dc, dt){
    this.hoten = ht;
    this.diachi = dc;
    this.dienthoai = dt;
    this.thongtin = function(){
        console.log(this.hoten , this.diachi, this.dienthoai);
    }
}
</script>
<script>
var k1 = new Khachhang("Lấp la lấp lánh", "123 tp Trăng Vàng", 0918123456);
k1.tuoi = 50;
k1.thongtin(); //Lấp la lấp lánh 123 tp Trăng Vàng 918123456
console.log(k1);//{hoten: "Lấp la lấp lánh", diachi: "123 tp Trăng Vàng", dienthoai: 918123456, tuoi: 50, thongtin: ƒ}
</script>

Cách tạo đối tượng bằng Object constructor functions như trên tương tự với các ngôn ngữ Java, C#, PHP… khác ở chỗ sử dụng hàm thay cho class.

Chú ý: Từ khóa this – trỏ đến thực thể (instance) vừa được tạo ra bởi cú pháp new

Để kế thừa giữa các lớp đối tượng, bạn cần sử dụng đến phương thức call(). Hàm call() giúp viết phương thức 1 lần. Sau đó thừa kế nó trong đối tượng khác mà không phải viết lại. Sau đây là ví dụ sử dụng hàm call() để thực hiện kế thừa giữa 2 đối tượng

<script>
    function Khachhang(ht, dc, dt){
        this.hoten=ht;
        this.diachi = dc;
        this.dienthoai = dt;
        this.thongtin = function(){
            console.log(this.hoten , this.diachi, this.dienthoai);
        }
    }
    function KhachhangVIP(ht, dc,dt, ns) {
        Khachhang.call(this,ht, dc, dt);
        this.ngaysinh = ns;
    }
</script>
<script>
var k2 = new KhachhangVIP( "Hằng Nga" , "tp Trăng Vàng", 0918123456, "27/4/2001"); 
console.log(k2.ngaysinh); // 27/4/2001
k2.thongtin(); //Hằng Nga tp Trăng Vàng 918123456
</script>

Mời bạn thực hiện thử nhé: tạo lớp VanDongVien bằng cách Object constructor functions. Gồm các thuộc tính : ma_VDV, hoten, ngay_sinh, giai_thuong, kiemTraDieuKien(), themGiaiThuong(), danhSachGiaiThuong() . Trong đó:

Phương thức kiemTraDieuKien() trả về true/false, nhận 2 tham số là: Chuỗi ngày tháng năm (ngày tổ chức giải đấu tiếp theo), Số tuổi đủ điều kiện tham gia thi đấu

=> Yêu cầu 1: viết code cho hàm để kiểm tra xem với ngày tháng năm sinh của mình, vận động viên có đủ điều kiện tham gia thi đấu hay không?

Thuộc tính giai_thuong là 1 mảng chứa các phần tử kiểu chuỗi để lưu lại các giải thưởng của vận động viên đạt được.

Phương thức themGiaiThuong() trả về số lượng giải thưởng của vận động viên, nhận tham số là Chuỗi tên giải thưởng của vận động viên nhận được

=> Yêu cầu 2: viết code cho phương thức themGiaiThuong() để thêm phần tử cho thuộc tính giai_thuong.

=> Yêu cầu 3: viết code cho phương thức danhSachGiaiThuong() để hiển thị danh sách các giải thưởng mà vận động viên đã đạt được

Sử dụng prototyping inheritance

Khái niệm prototype

Prototype là khuôn mẫu cho các đối tượng trong javascript. Các đối tượng kế thừa các thuộc tính và phương thức từ prototype của mình. Nếu bạn bổ sung thuộc tính hay phương thức vào prototype thì các đối tượng cũng sẽ có.

Trong ví dụ BaiTho là prototype của t1:

<script>
function BaiTho(tua, nd){
    this.tua = tua;
    this.noidung = nd;
}

var t1 = new BaiTho("Lục Vân Tiên", "Trước đèn xem chuyện Tây Minh");
t1.gia=12000;

console.log(t1); 
//BaiTho {tua: 'Lục Vân Tiên', noidung: 'Trước đèn xem chuyện Tây Minh', gia: 12000}
</script>

Nếu bổ sung thuộc tính hay hàm cho prototype thì các instance của nó sẽ dùng được ngay . Ví dụ dưới là cách bổ sung thẹm dodai tacgia vào prototype

<script>
function BaiTho(tua, nd){  this.tua = tua; this.noidung = nd; }
var t1 = new BaiTho("Lục Vân Tiên", "Trước đèn xem chuyện Tây Minh");
t1.gia=12000;

BaiTho.prototype.tacgia="Họ tên tác giả";
BaiTho.prototype.dodai = function(){
    return this.noidung.length; 
}
console.log(t1);  //Kết quả t1 như hình dưới:
</script>

Kế thừa prototype với __proto__

Chỉ thị __proto__ dưới đây cũng là cách bạn dùng có thể dùng khi cần chỉ định 1 đối tượng khác làm prototype để kế thừa cho đối tượng của mình.

<script>
sanpham = {
    tensp: "",
    usd: 0,
    vnd: function(tygia){ return this.usd*tygia },
    thongtin:function(){
        return `Tên SP: ${this.tensp} . Giá: ${this.usd}$`;
    }
} 
  
var laptop1 = {
    __proto__:sanpham,
    cannang: 1.4
}  
laptop1.tensp= "Lenovo ThinkBook 15IIL";
laptop1.usd = 515;
console.log(laptop1);
console.log("Giá vnđ: " , laptop1.vnd(25000));    
console.log("Thông tin: " , laptop1.thongtin());    
</script>

Đối tượng laptop1 ngoài các thuộc tính của mình (cannang) còn có đủ các thuộc tính và hàm kế thừa từ prototype sanpham

Như vậy có thể tạo trước 1 khung sườn (prototype). Sau đó sử dụng __proto__ để gắn khung sườn đó cho đối tượng. Các đối tượng kế thừa có thể sử dụng các phương thức/thuộc tính được định nghĩa từ khung sườn.

Xem thêm ví dụ sau để biết kế thừa prototype nhiều lần

<script>
sanpham = {
    tensp: "",
    usd: 0,
    vnd: function(tygia){ return this.usd*tygia },
    thongtin:function(){
        return `Tên SP: ${this.tensp} . Giá: ${this.usd}$`;
    }
} 

laptop = {
    __proto__:sanpham,      
    baohanh:function(){
        return "12 tháng"
    }
} 
var laptop1 = {          
    __proto__:laptop,
    cannang: 1.4
}  

laptop1.tensp= "Lenovo ThinkBook 15IIL";
laptop1.usd = 515;
console.log(laptop1);
console.log("Giá vnđ: " , laptop1.vnd(25000));    
console.log("Thông tin: " , laptop1.thongtin());    
</script>

D. Tạo đối tượng với Javasript class

Bạn có thể tạo 1 khuôn mẫu cho các đối tượng sắp tạo bằng từ khóa class. Trong class phải khai báo 1 hàm tên là constructor. Hàm này là hàm khởi tạo giá trị cho thuộc các thuộc tính.

Ngoài hàm constructor bắt buộc phải có, các hàm khác là tùy ý khai báo để dùng theo nhu cầu. Các hàm không được có chữ function ở đầu, không cách nhau bởi dấu phẩy. Xem cú pháp như sau:

class ClassName {
  constructor() { ... }
  ham_1() { ... }
  ham_2() { ... }
  ham_3() { ... }
}

Ví dụ cách tạo class với contructor và các phương thức:

<script>
class Sach {
    constructor(ten, gia, km) {
        this.tuasach = ten;
        this.gia = gia;
        this.km=km;
    } //constructor
    tien() {
        if (this.km==1) return this.gia*90/100;
        else return this.gia;
    } //tien
    vitri(loai) {        
        switch(loai){
            case 1: return "Tầng 1 , kệ 1,2,3"; break;
            case 2: return "Tầng 3 , kệ 8, 9"; break;
            default: return "Tầng 2 , kệ 4"; break;            
        }
    } //vitri
} //class
</script>

Chú ý: giữa các hàm không có dấu phẩy.

Tạo đối tượng theo mẫu class đã khai báo:

<script>
let b1 = new Sach("Mùi hương trầm", 190000, 0);
let b2 = new Sach("Nói với tuổi 20", 30000,  1);


console.log(b1); //Sach {tuasach: "Mùi hương trầm", gia: 190000, km: 0}
console.log(b1.tien(), b1.vitri(3)); //190000 "Tầng 2 , kệ 4"
console.log(b2); //Sach {tuasach: "Nói với tuổi 20", gia: 30000, km: 1}
console.log(b2.tien(), b2.vitri(1)); //27000 "Tầng 1 , kệ 1,2,3"
</script>

Lập trình hướng đối tượng trong javascript tức đối tượng hóa các đơn vị dữ liệu đang xử lý (khách hàng, sinh viên, sách, điện thoại, laptop, tin tức …… ) thành các đối tượng trogn Javascript. Mỗi đối tượng có nhiều thuộc tính (biến, hàm). Bài viết đã hướng dẫn các cách tạo đối tượng, bạn có thể tùy ý sử dụng theo nhu cầu.

E. So sánh mảng và đối tượng

Mảng và đối tượng giống nhau ở chỗ: cả hai đều là biến và chứa nhiều giá trị. Nhưng mảng thì đơn giản hơn, mảng chứa nhiều giá trị nhưng các giá trị đánh theo chỉ số (từ 0 ) còn các giá trị bên trong 1 đối tượng có tên (như giá, tựasách…) nên dễ hình dung hơn. hơn nữa trong 1 đối tượng bạn có thể tạo hàm để tính toán tạo ra những giá trị mới dựa trên những giá trị cũ đang có.

Thế nhưng ở mức độ đơn giản khi bạn cần 1 biến chỉ để chứa nhiều giá trị. thì bạn dùng mảng cho thích hợp hơn hơn. Tham khảo bài về mảng ở đây nhé Mảng trong javascript