Tôi không có tài năng gì cả. Tôi chỉ đam mê hiểu biết
Tìm Hiểu Prototype Trong Javascript

Tìm Hiểu Prototype Trong Javascript


Ngày 23 Tháng 5 Năm 2020

Hôm nay bạn hãy cùng mình đi vào tìm hiểu về khái niệm cũng như cách sử dụng prototype trong ngôn ngữ lập trình Javascript để phát triển web nhé.

Prototype Javascript là một trong những khái niệm cực kỳ quan trọng mà bạn cần phải nắm khi học về ngôn ngữ lập trình Javascript. Nó là prototype-based language(ngôn ngữ dựa trên nguyên mẫu) cho phép chia sẻ các thuộc tính và phương thức của một object thông qua một đối tượng tổng quát có khả năng nhân bản và mở rộng.

Nếu cảm thấy khó hiểu thì bạn có thể tìm hiểu thêm về prototype-based language ở đây nhé.

Và mình có lưu ý trước khi ta đi vào tìm hiểu về bài này, bạn cần nắm vững về khái niệm và cách sử dụng object trong javascript đã nhé. Nếu bạn muốn tham khảo hay xem lại về object thì nhấn ở đây nha.

Prototype Javascript Là Gì?

Trong javascript thì tất cả các object đều có prototype. Và các object đó sẽ kế thừa thuộc tính cũng như phương thức từ prototype của nó. Ví dụ như là :

  • Một object được tạo bằng new Date() thì nó sẽ kế thừa từ Date.prototype.
  • Một array được tạo bằng new Array() thì nó sẽ kế thừa từ Array.prototype.
  • Một object khachHang sẽ kế thừa từ khachHang.prototype.

Nhưng tất cả các object đó (Date, Array, Function...) thì đều được kế thừa từ Object.prototype. Để hiểu rõ hơn bạn xem hình ảnh bên dưới nhé:

Object.prototype trong javascript

Trong javascript, Các object(đối tượng) có một thuộc tính ẩn nội bộ là [[Prototype]] với giá trị là null hoặc tham chiếu đến một đối tượng khác. Lúc này các đối tượng được tham chiếu sẽ gọi là prototype(nguyên mẫu).

Khi chúng ta muốn lấy một thuộc tính nào đó trong object nhưng nếu nó không có trong object đó thì javascript sẽ tự động lấy nó từ prototype. Cách thức hoạt động này sẽ được gọi là prototypal inheritance (kế thừa nguyên mẫu).

Để dễ hình dung thì bạn có thể tưởng tượng prototype là một lớp cha và kế thừa nguyên mẫu sẽ là phương thức cho phép thằng con lấy và sử dụng các thuộc tính được thừa hưởng từ thằng cha.

Vậy làm sao chúng ta có thể thiết lập prototype cho một object và đọc thuộc tính từ nó?

Sử dụng __proto__

Đây là thuộc tính giúp chúng ta có thể truy cập và thiết lập [[prototype]] cho một đối tượng nào đó.
Thuộc tính này không phải là một tiện ích ở trong javascript đâu nhé mà nó được cung cấp bởi trình duyệt mà chúng ta đang sử dụng. Nhưng bạn cũng đừng quá lo lắng vì các trình duyệt phổ biến hiện tại thì đều hỗ trợ thuộc tính __proto__.
Bây giờ chúng ta sẽ cùng nhau đi vào ví dụ cụ thể để dễ hình dung hơn nhé:

Đoạn Code:

  let dongVat = {
   an : true
 };
 let meo = {
   chay: true
 };
 meo.__proto__ = dongVat;
 
 console.log(meo.an);
 console.log(meo.chay);

Kết Quả:

__proto__ trong javascript

Ở đây mình chọn động vật là thằng cha, mèo sẽ là thằng con. Vậy để kế thừa thuộc tính của thằng cha thì chúng ta sử dụng đoạn code như trên là:
meo.__proto__ = dongVat;
Lúc này phương thức kế thừa nguyên mẫu sẽ hoạt động như sau:

  • Khi chúng ta gọi meo.an thì javascrip sẽ tìm trong đối tượng meo có thuộc tính an hay không.
  • Nếu không có, thì javascrip sẽ tiếp tục tham chiếu đến [[prototype]] mà bây giờ chính là đối tượng dongVat.
  • Nó tiếp tục tìm trong đối tượng dongVat nếu thấy thuộc tính an thì trả về kết quả.
Bạn có thể xem hình bên dưới để hiểu rõ hơn nhé:

Ví dụ __proto__ trong prototype javascript

Ở đây chúng ta có thể nói là dongVat sẽ là một prototype(nguyên mẫu) của meo hay meo là kế thừa nguyên mẫu từ dongVat.
Không những chúng ta có thể sử dụng thuộc tính mà còn thừa kế phương thức từ prototype nữa. Để hiểu rõ hơn bạn xem đoạn code sau nhé:

Đoạn Code:

  let dongVat = {
   an : true,
   uongNuoc() {
    console.log("Động vật uống nước");
    }
 };
 let meo = {
   chay: true
 };
 meo.__proto__ = dongVat;
 
 meo.uongNuoc();

Kết Quả:

Kế thừa phương thức bằng __proto__ trong javascript

Như bạn thấy là javascript sẽ tự động tìm kiếm phương thức từ prototype nếu phương thức đó không tồn tại trong object meo. Để hiểu rõ hơn bạn xem hình ảnh dưới đây nhé:

Lấy phương thức bằng __proto__ trong prototype javascript

Mình có lưu ý nhỏ là prototype chỉ cho phép chúng ta đọc thuộc tính. Do đó nếu bạn muốn xác định lại một phương thức nào đó cho đối tượng kế thừa thì chúng ta có thể gán trực tiếp vào trong object đó. Để hiểu rõ hơn bạn xem đoạn code sau đây nhé:

Đoạn Code:

  let dongVat = {
   an : true,
   uongNuoc() {
    console.log("Động vật uống nước");
    }
 };
 let meo = {
   chay: true
 };
 meo.__proto__ = dongVat;
meo.uongNuoc = function() {
  console.log("Mèo uống nước!");
};
 meo.uongNuoc();

Kết Quả:

Xác định lại phương thức bằng __proto__ trong javascript

Như bạn thấy thì khi chúng ta gọi meo.uongNuoc(); thì nó sẽ tìm phương thức đó trong đối tượng meo và thực hiện ngay lập tức.
Chỉ khi nào nó không tìm thấy phương thức trong đối tượng meo thì lúc đó javascript mới sử dụng đến prototype.

Xác định lại phương thức bằng __proto__ trong prototype javascript

Sử Dụng this Prototype Javascript

Khi bạn có một object cha chứa nhiều phương thức, thuộc tính và được kế thừa bởi nhiều object con thì các phương thức trong object cha phải được viết dạng tổng quát để có thể được sử dụng cho nhiều object con.
Ví dụ bạn có một mô hình kế thừa như sau:

Xác định lại phương thức bằng __proto__ trong prototype javascript

Vậy cứ mỗi khi có một object con mới thì chúng ta phải xác định lại phương thức an để phù hợp với object đó. Trong thực tế thì cách này rất ít khi được sử dụng vì nó sẽ gây ra sự cồng kềnh khó hiểu cho đoạn mã cũng như gây lãng phí thời gian, công sức của bạn.

Để giải quyết vấn đề trên thì bạn có thể sử dụng từ khóa this. Nó giúp chúng ta ghi hàm một cách tổng quát và có thể gọi cho bất kỳ object con nào. Bây giờ chúng ta thử đi vào ví dụ để hiểu rõ hơn nhé:

Đoạn Code:

  let dongVat = {
   ten: "Động Vật",
   uongNuoc() {
    console.log(this.ten + " đang uống nước");
    }
 };
 let meo = {
   ten: "Mèo"
 };
 let cho = {
   ten: "Chó"
 };
 meo.__proto__ = dongVat;
 cho.__proto__ = dongVat;

meo.uongNuoc();/*Mèo đang uống nước*/
cho.uongNuoc();/*Chó đang uống nước*/

Kết Quả:

Kế thừa phương thức btrong prototype javascript

Như bạn thấy thì các object meocho có thể sử dụng phương thức chung của dongVat mà không cần phải định nghĩa lại phương thức cho những object con đó. Khi bạn thêm các object khác như chim, khi... thì chúng ta đều sử dụng lại được phương thức uongNuoc trong dongVat.

Sử Dụng Vòng Lặp Cho Prototype Javascript

Khi bạn cần lặp qua các thuộc tính ở trong object con và những object mà nó được kế thừa thì chúng ta có thể sử dụng vòng lặp for...of. Để nắm rõ hơn bạn xem đoạn code sau nhé:

Đoạn Code:

  let dongVat = {
   an : true
 };
 let meo = {
   chay: true
 };
 meo.__proto__ = dongVat;
 
for(let thuocTinh in meo) console.log(thuocTinh);

Kết Quả:

for...of lấy thuộc tính trong object javascript

Nếu như bạn muốn lấy thuộc tính object con đó sở hữu thì có thể sử dụng phương thức Object.keys(). Để hiểu rõ hơn chúng ta cùng đi vào ví dụ cụ thể sau đây nhé:

Đoạn Code:

  let dongVat = {
   an : true
 };
 let meo = {
   chay: true
 };
 meo.__proto__ = dongVat;
 
console.log(Object.keys(meo));

Kết Quả:

Sử dụng Object.keys() trong object javascript

Thuộc Tính Prototype Trong Function Javascript

Trước khi javascript cho phép chúng ta thiết lập prototype bằng __proto__. Thì ngày xưa ta có một cách khác để thiết lập là sử dụng thuộc tính "prototype" của constructor function. Mục đích của mình khi nói về phần này là giúp bạn có thể hiểu và sử dụng khi lỡ gặp nó trong quá trình lập trình.

Trước khi đi vào vấn đề chính thì bạn cần hiểu và sử dụng được constructor function đã nhé. Nếu bạn muốn xem lại hay tham khảo thêm thì nhấn vào đây nha.

Bây giờ chúng ta sẽ đi vào ví dụ cụ thể để dễ hình dung hơn nhé:

Đoạn Code:

  let dongVat= {
   an: true
 };
 function Meo(ten) {
   this.ten = ten;
 }

 Meo.prototype = dongVat;
/*Cách gán phía dưới bằng với phương thức meo.__proto__ = dongVat*/
 let meo = new Meo("Mèo Đen");
 console.log(meo.an);
console.log(meo.ten);

Kết Quả:

Constructor function prototype javascript

Mình có lưu ý là chỉ khi (Function).prototype là một object thì chúng ta mới có thể sử dụng từ khóa new để thiết lập [[Prototype]] đến cho một đối tượng mới. Như ví dụ trên là chúng ta gán Meo.prototype = dongVat. Thì tất cả các đối tượng được tạo từ hàm Meo sẽ thừa hưởng được các thuộc tính và phương thức của dongVat. Và để dễ hình dung hơn bạn xem hình ảnh sau nhé:

Hình ảnh minh họa về prototype function javascript

Prototype Mặc Định Trong Function Javascript

Tất cả các function trong javascript đều có thuộc tính prototype mặc định được định nghĩa sẵn dù chúng ta có thiết lập cho nó hay không.
Thuộc tính prototype mặc định này là một object. Nó chỉ có một thuộc tính là constructor để trỏ về lại hàm của nó. Để hiểu rõ hơn bạn xem đoạn code sau nhé:

Đoạn Code:

 function Meo() {}
/*prototype mặc định*/
/*Meo.prototype = {constructor: Meo};*/

console.log(Meo.prototype.constructor == Meo)

Kết Quả:

prototype.constructor trong function javascript

Để dễ hình dung bạn xem hình ảnh miêu tả ở dưới đây nhé:

prototype constructor function javascript

Và khi chúng ta tạo một object bằng function (hàm) với từ khóa new thì tất cả các object đó đều kế thừa thuộc tính constructor thông qua [[Prototype]]. Để hiểu rõ hơn bạn xem đoạn code sau đây nhé:

Đoạn Code:

 function Meo() {}
/*prototype mặc định*/
/*Meo.prototype = {constructor: Meo};*/

let meoDen = new Meo();
console.log(meoDen.constructor == Meo);

Kết Quả:

 Ví dụ prototype.constructor trong function javascript

Để dễ hiểu hơn bạn xem hình ảnh miêu tả dưới đây nhé:

prototype constructor function 2 javascript

Object.prototype Javascript

Như bạn đã biết thì tất cả các object trong javascript đều tham chiếu đến Object.prototype. Phần này chúng ta sẽ đi vào tìm hiểu Object.prototype có những gì nhé.

Để xem được Object.prototype thì đơn giản bạn chỉ cần tạo một object trong Javascript như đoạn code sau đây:

Đoạn Code:

 let doiTuong = {}; 
console.log(doiTuong);

Kết Quả:

Xem Object.prototype trong javascript

Để thấy được Object.prototype() thì bạn nhấn vào dấu mũi tên trong console ở google developer để hiển thị các phương thức, thuộc tính của nó nhé.
Như ví dụ trên ta có thể thấy nó có các thuộc tính như là toString(), valueOf()... và cũng có thuộc tính tên constructor để trỏ về hàm Object(). Khi việc tạo mới một đối tượng bằng new Object() hay {} thì [[prototype]] sẽ được thiết lập và tham chiếu đến Object.prototype(). Do đó object doiTuong có thể sử dụng các phương thức từ Object.prototype().
Để dễ hình dung bạn xem hình ảnh sau đây nhé:

Minh họa Object.prototype

Bây giờ chúng ta thử gọi thuộc tính toString() được lấy từ Object.prototype bằng đoạn code dưới đây nhé:

Đoạn Code:

 let doiTuong = {}; 
console.log(doiTuong.toString());
console.log(typeof doiTuong.toString());

Kết Quả:

Phuong thức toString() Object.prototype trong javascript

Đối với Object.prototype thì nó sẽ không có [[Prototype]] nào nữa. Bạn có thể hình dung như sau, nó là đối tượng lớn nhất trong javascript và chỉ có các object khác kế thừa từ nó chứ nó không kế thừa bất cứ đối tượng nào trong Javascript. Bây giờ để bạn hiểu rõ hơn thì chúng ta sẽ sử dụng __proto__ để kiểm tra thử nhé:

Đoạn Code:

console.log(Object.prototype.__proto__); //Kết quả sẽ là null

Kết Quả:

Kiểm tra Object.prototype bằng __proto__

Tại Sao Mọi Thứ Đều Kế Thừa Từ Object Trong Javascript?

Phần này mình sẽ giúp bạn trả lời câu hỏi tại sao mọi thứ đều kế thừa từ object trong ngôn ngữ lập trình javascript. Khi bạn tạo một mảng (array), ngày (date), hàm (function) hay các đối tượng khác thì các phương thức sẽ được giữ trong prototype tương ứng của nó.

Nghĩa là khi bạn tạo một mảng với phần tử là [1, 2, 3] thì mặc định javascript sẽ tự động thiết lập new Array() để lưu giữ các phương thức trong Array.prototype. Để dễ hình dung bạn xem hình ảnh sau đây sẽ rõ nhé:

Ví dụ về cấp bậc prototype javascript

Nhưng như vậy chúng ta cũng chưa thể khẳng định cho câu định nghĩa trên vì các kiểu dữ liệu nguyên thủy như số, chuỗi, boolean... thì nó có kế thừa Object.prototype hay không?

Mặc dù chúng không phải là kiểu dữ liệu object nhưng nếu chúng ta thử truy cập thuộc tính của nó thì các object sẽ được tạo ra bằng cách sử dụng constructor Number, String, Boolean. Và những object này sẽ bị ẩn đi và chúng ta chỉ thấy được nó khi sử dụng __proto__. Để dễ hiểu bạn xem đoạn giải thích về chuỗi sau đây nhé.

Bạn còn nhớ chúng ta có thể đếm ký tự trong chuỗi bằng lệnh (Chuỗi).length. Nhưng chuỗi là kiểu dữ liệu nguyên thủy vậy thì phương thức này được lấy từ đâu?

Trong trường hợp này thì giá trị chuỗi sẽ được ép vào một đối tượng String nên chúng ta có thể sử dụng được phương thức đó thông qua String.prototype.

Bây giờ chúng ta thử đi vào xem String.prototype bằng đoạn code bên dưới nhé:

Đoạn Code:

 let chuoi = "Xin Chào Các Bạn";
console.log(chuoi.__proto__);

Kết Quả:

Kiểm tra String.prototype bằng __proto__

Như bạn thấy nó có rất nhiều phương thức được kế thừa từ String.prototype. Và khi nó đã là object thì nó cũng sẽ kế thừa từ Object.prototype. Để dễ hình dung bạn xem hình ảnh sau đây nhé:

Ví dụ String.prototype javascript

Bây giờ chúng ta có thể trả lời câu hỏi trên rồi nhé.

Không Sử Dụng __proto__

Ngày nay thì __proto__ thì ít được sử dụng hơn vì trong phiên bản Javascript mới thì có cung cấp thêm cho chúng ta một số phương thức tiện ích hơn như là:

  • Object.create(): Tạo một đối tượng rỗng, sử dụng đối tượng hiện có để làm prototype (nguyên mẫu) cho đối tượng đó.
  • Object.getPrototypeOf(): Trả về prototype của một đối tượng xác định.
  • Object.setPrototypeOf(): Thiết lập prototype của một đối tượng xác định cho một đối tượng khác.

Để hiểu rõ hơn bạn xem đoạn code sau nhé:

Đoạn Code:

 let dongVat= {
an: true
};
/* Tạo một đối tượng mới với dongVat là prototype */
let meo = Object.create(dongVat);
console.log(meo.an);
/*Kiểm tra prototype cua meo*/
console.log(Object.getPrototypeOf(meo) === dongVat);/*true*/
/*Thiết lập thuộc prototype khác cho đối tượng meo*/
Object.setPrototypeOf(meo, {}); // thay đổi prototype cua meo đến {} (Object)

Kết Quả:

Object prototype create get set

Tổng kết:

Qua đây mình mong bài viết sẽ giúp bạn hiểu được khái niệm cũng như cách sử dụng prototype trong ngôn ngữ Javascript và nếu có thắc mắc gì cứ gửi email mình sẽ phản hồi sớm nhất có thể. Rất mong bạn tiếp tục ủng hộ trang web để mình có thể viết nhiều bài hay hơn nữa nhé. Chúc bạn có một ngày vui vẻ!

Load WooCommerce Stores in 249ms!