[ Javascript ] Prototype trong Javascript là gì, và tại sao nó lại quan trọng?

Prototype là một khái niệm cơ bản và cốt lõi của ngôn ngữ Javascript, bất kì ai muốn nắm vững ngôn ngữ này đều phải hiểu về khái niệm prototype trong Js. Javascript không kế thừa kiểu class-based mà kế thừa trong Javascript là dựa vào protype (từ ES5 trở về trước), điều này khiến prototype trở nên rất quan trọng.

Để hiểu được prototype, trước hết bạn cần phải hiểu về Object trong Javascript. Bạn có thể xem qua bài viết Tìm hiểu về Object trong JS để ôn lại các kiến thức về object trước khi bắt đầu.

Prototype là cái gì?

Trước hết, hãy để ý rằng bản thân prototype là một đối tượng object trong JS, được gọi là prototype object (đối tượng prototype). (Cần chú ý điều này để tránh nhầm lẫn với thuộc tính prototype của function)

Tất cả các object trong Js đều có một prototype, và các object này kế thừa các thuộc tính (properties) cũng như phương thức (methods) từ prototype của mình.

Nếu bạn vẫn chưa hiểu được gì thì cũng đừng vội, javascript vốn dĩ lằng nhằng khó ưa, nhưng qua được đoạn đầu sẽ thấy dễ chịu hơn rất nhiều.

Nhắc lại 1 chút về các khái niệm kì cục trong Javascript

Thứ 1: Trong Js, bản thân 1 hàm (1 function) cũng được coi là 1 object, và function có một thuộc tính (property) gọi là thuộc tính prototype, bản thân thuộc tính prototype này mang giá trị là 1 object. (Chú ý: một instance object thì không có thuộc tính prototype)

Thứ 2: Bởi vì ta dùng function để tạo ra 1 mẫu khởi tạo đối tượng, do đó thuộc tính prototype của function có 1 khả năng đặc biệt: bạn sẽ thêm các thuộc tính (property) hoặc phương thức (method) vào thuộc tính prototype của function khởi tạo để thực hiện kế thừa, tất cả các đối tượng con tạo ra bởi hàm khởi tạo đều mang các giá trị trong thuộc tính prototype của hàm này. (Xem lại cách tạo ra object dùng prototype ở bài NÀY).

Chính bởi 2 khái niệm trên, mà có thể xem mẫu khởi tạo (constructor function) là 1 đối tượng prototype, bản thân nó có thuộc tính prototype. Kế thừa trong Javascript thuộc kiểu prototype-based (không giống với class-based như các ngôn ngữ OOP khác).

Thứ 3: Các object trong Javascript có một khái niệm gọi là đặc tính prototype của đối tượng (prototype attribute), đặc tính này có giá trị trỏ tới prototype object mà nó kế thừa thuộc tính. Ta dùng thuộc tính “__proto__” để truy cập tới prototype object.

prototype.jpeg

Tạo ra Prototype như thế nào?

Như đã nói tới ở trên, do hàm khởi tạo đối tượng cũng được xem là 1 đối tượng prototype, do đó các đơn giản để tạo ra 1 đối tượng prototype là khai báo một hàm khởi tạo:

//Tạo ra 1 mẫu khởi tạo, cũng là tạo ra 1 prototype object
function Person(_age, _name){
   this.age = _age;
   this.name = _name;
}

//Có thể thêm thuộc tính vào thuộc tính prototype của hàm khởi tạo
Person.prototype.height = 0;

//Tạo ra 1 instance của Person
//Có cả 3 thuộc tính của mẫu khởi tạo Person
var john_person = new Person(10, "John");
for (var att in john_person){
   console.log(att);
}

//Xem đối tượng prototype của instance vừa tạo
john_person.__proto__;

Đoạn code trên vừa tạo ra một hàm khởi tạo là hàm Person(_age, _name), thuộc tính prototype của hàm này lại chứa thuộc tính “height”. Do đó một đối tượng được tạo ra từ hàm khởi tạo này ta sẽ có 3 thuộc tính: age, name, và height.

Nếu truy cập prototype object của object vừa tạo (instance vừa tạo), thì ta thấy object này là một object chứa 1 hàm khởi tạo và 1 thuộc tính (thuộc tính “height”).

Nhắc lại về cách tạo object

Như đã nhắc tới trong bài viết tìm hiểu về object trong Js, ta có cách cách sau để tạo ra object: dùng object literal, dùng constructor của đối tượng Object, hoặc tự tạo một constructor function (mẫu khởi tạo).

Những cách này về mặt cách thức thì khác nhau, nhưng về bản chất chúng đều dùng tới hàm khởi tạo và các thuộc tính prototype của hàm này để tạo ra một đối tượng mới: object literal và Object constructor sử dụng Object() và Object.prototype, nếu dùng mẫu khởi tạo thì là mauKhoiTao() và mauKhoiTao.prototype.

Tại sao prototype lại quan trọng trong Javascript?

Thuộc tính prototype của function: cơ chế kế thừa trong Javascript

Từ các phiên bản ES5 trở về trước, Javascript không có khái niệm class, và do vậy mà nó không thể thực hiện việc kế thừa để mở rộng ứng dụng như các ngôn ngữ OOP khác. Tuy nhiên, prototype giúp chúng ta thực hiện kế thừa theo một cách gần tương tự như thế: Javascript thực hiện kế thừa theo cơ chế prototype-based.

Nói ngắn gọn, để thực hiện kế thừa trong Js, bạn cần tạo 1 hàm khởi tạo, sau đó thêm các thuộc tính và phương thức vào thuộc tính prototype của hàm khởi tạo này. Các instance tạo ra bởi hàm khởi tạo này sẽ chứa các thuộc tính và phương thức được định nghĩa ở trên. Đoạn code sau minh hoạ cho điều này:

//Tạo ra 1 hàm khởi tạo cơ sở (tựa như lớp cơ sở)
function Animal(_age){
   this.age = _age;
}

//Có thể thêm thuộc tính vào thuộc tính prototype của hàm khởi tạo
Animal.prototype.showAge = function(){
   console.log( this.age );
};

//Tạo ra 1 hàm khởi tạo con (sẽ dùng để kế thừa lớp cơ sở)
function Bird(_color){
   this.color = _color;
}
//Thực hiện kế thừa, gán hàm khởi tạo của Animal cho prototype của Bird
Bird.prototype = new Animal(5);
Bird.prototype.showColor = function(){
   console.log( this.color );
};

//Kiểm tra sự kế thừa
var eagle = new Bird('red');
eagle.age = 5;
eagle.showAge();       //5
eagle.showColor();     //red

Ở ví dụ trên, đối tượng eagle sử dụng được hàm showAge() của Animal prototype bởi vì ta đã gán hàm khởi tạo của Animal vào prototype của Bird.

Đây chính là cơ chế kế thừa trong Javascript, đối tượng eagle đã kế thừa những gì có trong prototype của nó (là Bird.prototype), và nó cũng kế thừa luôn những gì có trong thuộc tính prototype của Bird (chính là Animal.prototype).

Đặc tính prototype của object: truy cập vào các thuộc tính của đối tượng

Prototype rất quan trọng trong việc giúp ta truy cập tới các thuộc tính và phương thức của đối tượng. Đặc tính prototype của đối tượng (hay còn gọi là prototype object) là một “object cha” nơi chứa các thuộc tính và phương thức được kế thừa. Vì thế, khi ta gọi tới một thuộc tính của đối tượng (vd: eagle.age), ban đầu Js sẽ tìm trong thuộc tính riêng của đối tượng, nếu không tìm thấy, nó sẽ tiếp tục tìm trong prototype của đối tượng, và lặp lại tiếp với prototype của đối tượng prototype, … Quá trình lặp lại này được gọi là chuỗi prototype trong Javascript. Chính điều này + thuộc tính prototype của function tạo nên cơ chế kế thừa prototype-based cho Javascript.

18 thoughts on “[ Javascript ] Prototype trong Javascript là gì, và tại sao nó lại quan trọng?

  1. Cảm ơn anh :D.. Bài viết khá là bổ ích cho những ai có mong muốn tìm hiểu về Javascript ạ :D..
    Em muốn hỏi anh một chút về phần những khái niệm kỳ cục trong JS, “(Chú ý: một instance object thì không có thuộc tính prototype)” đoạn này ý là sao anh nhỉ? Là một instance trong js không có thuộc tính proto à anh?

    Like

    1. Cảm ơn e đã phản hồi.
      Giả sử ta viết
      var func1 = new function() {};
      var instance1 = new func1();
      Khi đó func1 là một function, cũng là 1 object, bởi vì nó là function nên nó có thuộc tính prototype, tức là gán func1.prototype = ABC gì đó là hợp lệ.
      Còn instance1 nó chỉ là object mà ko phải là function (gọi là instance object), nó ko có thuộc tính prototype nên khi gọi instance1.prototype = ABC sẽ bị lỗi, ta truy cập tới đặc tính prototype của nó bằng cách gọi instance1.proto, do vậy câu lệnh instance1.proto.prototype = ABC thì chạy được.

      Thắc mắc của e đã đc giải đáp chưa nhỉ?

      Like

      1. (y), dạ oke em hiểu rồi ạ :D.
        Là nó sẽ chạy là kiểu:
        var a = function () {};
        thì a có thuộc tính prototype, còn instance của cái này thì không có thuộc tính đó.
        var b = new a();
        thì b.prototype == undefined.
        Oke tks anh ạ :D..

        Liked by 1 person

  2. Pingback: #03. Javascript hướng đối tượng - Prototype trong Javascript - Nguyễn Phú Hảo

  3. Trần Quang Hiền

    Wow! Bài viết rất bổ ích cho người mới học JavaScript như em. Tuy có vài chỗ em không hiểu nhưng bài viết đã cung cấp rất nhiều kiến thức bổ ích về Prototype. Thanks anh

    Liked by 1 person

  4. Hieu

    Bird.prototype = new Animal();

    Bạn cho mình hỏi, sao viết được thế này nhỉ. Mình chưa hiểu chỗ này lắm. Mình chỉ hiểu prototype sinh ra là để mình thêm thuộc tính & phương thức có tính thừa kế thôi.

    Like

    1. Lệnh “new Animal(5)” vẫn trả về một số thuộc tính và phương thức, nên bạn vẫn gán được nhé.
      Bạn có thể console.log(eagle) ra để xem kĩ hơn bên trong có gì nhé.

      Like

  5. Alice Keith Edwards

    function Person(_age, _name){
    this.age = _age;
    this.name = _name;
    }

    //Có thể thêm thuộc tính vào thuộc tính prototype của hàm khởi tạo
    Person.prototype.height = 0;

    em chào Anh, em hiểu là mội thứ trong Javascipt là object.
    ở đây có hàm Person(), nó cũng là một object, protype là một thuộc tính của nó,
    nhưng em không hiểu:” //Có thể thêm thuộc tính vào thuộc tính prototype của hàm khởi tạo”.
    đoạn này ý là sao ạ, thuộc tính height là thuộc tính của prototype hay thuộc tính của Person?

    Like

    1. Cảm ơn e đã hỏi

      //Có thể thêm thuộc tính vào thuộc tính prototype của hàm khởi tạo
      Person.prototype.height = 0;

      Ở đây:
      – Hàm khởi tạo: Person
      – Thuộc tính prototype của hàm khởi tạo: Person.prototype
      Vậy đoạn trên có nghĩa là: thêm thuộc tính “height” vào Person.prototype
      Khi đó, nếu ta tạo 1 đối tượng mới:
      var person = new Person(x, y);
      Thì biến person sẽ có 3 thuộc tính:
      – Thứ nhất: person.age mang giá trị x
      – Thứ 2: person.name mang giá trị y
      – Thứ 3: person.height mang giá trị 0 (thuộc tính này là thuộc tính được kế thừa từ prototype, có nghĩa là mọi instance được khởi tạo từ Person đều mang giá trị height giống nhau là 0)

      Like

Leave a comment