Trong thế giới lập trình hướng đối tượng (OOP) của JavaScript, chúng ta thường xuyên nghe đến các khái niệm như class, prototype, proto, prototype chain, constructor, inheritance… Thậm chí, chúng còn liên quan đến các pattern như factory pattern, constructor pattern, prototype pattern. Vậy mối quan hệ giữa những khái niệm này là gì? Các mô hình hoạt động ra sao và phát triển như thế nào? Bài viết này sẽ từng bước giải thích OOP trong JS, tập trung vào Prototype, một khái niệm then chốt.
Mục Lục
Prototype là gì?
Để hiểu rõ hơn về Prototype, hãy bắt đầu với một ví dụ thực tế (hơi hài hước) mà nhiều người có thể đã từng trải qua.
Ví dụ: Một chàng trai dẫn bạn gái đi mua sắm, nhưng cô gái không có tiền. Tuy nhiên, cô vẫn mua được đồ vì chàng trai trả tiền. Trong trường hợp này, cô gái đang “lợi dụng” ví tiền của chàng trai.
Mô tả bằng code:
var bangai = { name: 'dao thi mo' };
var thangdaigai = {
name: 'thang dai gai',
pay: function() {
console.log('Da mua boi tien cua bo me');
}
};
Object.setPrototypeOf(bangai, thangdaigai);
bangai.pay(); //Da mua boi tien cua bo me
Ví dụ trên cho thấy bangai (cô gái) đã sử dụng method pay() của thangdaigai (chàng trai). Chương trình chỉ ra rằng prototype của bangai chính là thangdaigai. bangai không có method pay(), nhưng nó đã “lợi dụng” method này từ thangdaigai.
Vậy, Prototype là gì? Có thể hiểu Prototype là một mối quan hệ ủy nhiệm. Hoặc, bạn cũng có thể hiểu nó là sự kế thừa.
Để hiểu rõ hơn, hãy xem một ví dụ khác:
var bangai = {
name: 'dao thi mo',
pay: function() {
console.log('Da mua hang');
}
};
var thangdaigai = {
name: 'thang dai gai',
pay: function() {
console.log('Da mua boi tien cua bo me');
}
};
bangai.pay(); //Da mua hang
Trong ví dụ này, bangai có method pay() riêng. Do đó, khi gọi bangai.pay(), method của chính bangai sẽ được thực thi, không liên quan đến thangdaigai.
Đến đây, bạn đã có thể hình dung được Prototype là gì. Bản chất của nó là vậy. Hãy tiếp tục với một ví dụ khác.
Ví dụ: Chúng ta có một function như sau:
function Cat(name, color){
this.name = name;
this.color = color;
this.type = "dongvat";
this.eat = function(){
console.log(' an ca');
}
}
var cat1 = new Cat('Sen', 'pink')
console.log(cat1.eat()) // an ca
console.log(cat1.type) // dongvat
var cat2 = new Cat('Boss', 'black');
console.log(cat2.eat()) // an ca
console.log(cat2.type) // dongvat
Nhìn bề ngoài, code có vẻ không có vấn đề gì. Tuy nhiên, có một nhược điểm lớn: Với mỗi đối tượng (object) được tạo ra từ function Cat, các thuộc tính type và method eat() hoàn toàn giống nhau. Mỗi lần tạo một đối tượng mới, nội dung này lại được lặp lại, gây tốn bộ nhớ. Điều này không hiệu quả và không phù hợp với mô hình OOP.
Để chứng minh điều này, hãy kiểm tra:
console.log(cat1.eat === cat2.eat); //false
Kết quả là false, nghĩa là cat1.eat và cat2.eat thực tế là hai function khác nhau, chiếm hai vùng nhớ khác nhau.
Chính vì điều này, Prototype trở nên quan trọng. Tất cả các thuộc tính và phương thức được định nghĩa trên prototype của đối tượng sẽ được “kế thừa” bởi các đối tượng được tạo ra từ hàm tạo (constructor function). Điều này có nghĩa là chúng ta có thể định nghĩa các thuộc tính và phương thức dùng chung trực tiếp trên prototype của đối tượng.
Chúng ta có thể viết lại ví dụ trên như sau:
function Cat(name, color){
this.name = name;
this.color = color;
}
Cat.prototype.type = 'dongvat';
Cat.prototype.eat = function() {
console.log('an ca');
}
var cat1 = new Cat('Sen', 'pink')
console.log(cat1.eat())
console.log(cat1.type)
var cat2 = new Cat('Boss', 'black');
console.log(cat2.eat())
console.log(cat2.type)
Trong trường hợp này, các thuộc tính type và method eat() của tất cả các đối tượng Cat thực sự là cùng một địa chỉ bộ nhớ, trỏ đến prototype của đối tượng. Do đó, hiệu quả hoạt động được cải thiện.
Kiểm tra lại:
console.log(cat1.eat === cat2.eat); //true
Kết quả bây giờ là true, chứng tỏ cat1.eat và cat2.eat là cùng một function.
Tổng kết
Prototype trong JavaScript là một cơ chế cho phép các đối tượng kế thừa các thuộc tính và phương thức từ một đối tượng khác. Việc sử dụng Prototype giúp tiết kiệm bộ nhớ và tăng hiệu quả hoạt động của chương trình, đồng thời là nền tảng quan trọng để xây dựng các ứng dụng hướng đối tượng phức tạp.
Hy vọng bài viết này đã giúp bạn hiểu rõ hơn về khái niệm Prototype trong JavaScript.
