THÔNG DỤNG
OOP
ES6
ES(X)
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
Dự án mới của mình là gamehow.net, mời anh em ghé thăm và góp ý ạ.

Đọc và hiểu về prototype trong Javascript trong 10 phút

Trong bài này chúng ta sẽ tìm hiểu prototype trong Javascript, đây là một phần kiến thức quan trọng, nó giúp các bạn làm việc với các object và class hiệu quả hơn.

Qua bài này bạn sẽ hiểu được khái niệm prototype là gì? Cách sử dụng prototype hiệu quả trong quá trình làm việc với Javascript. Chỉ mất 5 phút là bạn sẽ hiểu được thuật ngữ này.

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Lưu ý: Bạn cần đọc bài viết prototype trong javascript là gì để hiểu về nó rồi mới xem bài này nhé.

1. Hiểu về prototype trong javascript

Trong Javascript, prototype được đề cập đến một hệ thống dữ liệu của đối tượng, qua đó cho phép bạn xác định các thuộc tính và phương thức trên các đối tượng có thể truy cập được qua các phiên bản của đối tượng (instance).

Prototype có liên quan chặt chẽ với lập trình hướng đối tượng, nên nếu bạn đã từng học qua các ngôn ngữ như C++ hay Java thì sẽ là một lợi thế rất lớn. Còn nếu bạn chưa hiểu thì mình xin giải thích một chút như sau.

Lập trình hướng đối tượng là kỹ thuật đưa các đối tượng trong thực tế vào trong chương trình máy tính thông qua những dòng code. Tùy vào các chương trình mà có các đối tượng khác nhau. Ví dụ bạn đang xây dựng chương trình quản lý sinh viên thì chúng ta có các đối tượng như: Sinh viên, Khoa, Lớp, Trường, Giáo Viên ...

Quay trở lại vấn đề chính là javascript prototype nhé.

Giả sử mình có các mảng như sau:

const array = ['one', 'two', 'three'];
// Hoặc sử dụng lệnh new Array()
const array = new Array('one', 'two', 'three');

Bây giờ mình sẽ sử dụng lệnh console.log để xem biến array có những gì nhé.

console.log(array);

javascript prototype JPG

Như bạn thấy, ngoài các giá trị mà mình đã gán vào thì không có một phương thức hay thuộc tính nào khác, nhưng ta vẫn có thể sử dụng các phương thức như concat, slice, filter, và map trên đối tượng array này.

Tại sao lại như vậy?

Bởi vì các phương thức trên nằm trong phần prototype của array. Bạn có thể xem nó bằng cách mở rộng mục __proto__ mà mình đã chụp hình ở trên.

array prototype javascript JPG

Trên là danh sách các thuộc tính và phương thức có sẵn trong prototype của Array. Vì vậy, mỗi khi bạn tạo một array mới thì nó đều được tích hợp sẵn, và array mới này ta gọi là một thể hiện (instance) của đối tượng Array.

Khi bạn gọi array.map thì javascript sẽ tìm phương thức map trong danh sách thuộc tính của nó. Nếu không tồn tại thì nó sẽ dò tìm trong phần prototype.

Vì vậy, định nghĩa chính xác của prototype đó là: Prototype là một đối tượng chứa các thuộc tính và phương thức, qua đó các instance sẽ tìm kiếm trong trường hợp thuộc tính cần tìm không tồn tại trong instance.

2. Sơ đồ hoạt động của prototype trong javascript

Trong javascript, khi bạn truy cập vào một thuộc tính / phương thức thì nó sẽ hoạt động theo các bước như sau:

Bước 1: JavaScript sẽ kiểm tra xem thuộc tính có sẵn bên trong đối tượng hay không. Nếu có, JavaScript sử dụng thuộc tính này và kết thúc, nếu không có thì nhảy qua bước 2.

Bước 2: Javascript sẽ kiểm tra trong prototype của đối tượng đó có hay không? Nếu có thì sử dụng và kết thúc, không có thì nhảy qua bước 3.

Bước 3: Nếu đến bước 2 mà vẫn không tìm thấy thì sẽ có hai trường hợp xảy ra:

  • Trả về undefined nếu bạn đang truy cập thuộc tính.
  • Thông báo lỗi nếu bạn truy cập vào phương thức.

Bây giờ ta sẽ làm một ví dụ để mô phỏng cho sơ đồ hoạt động của prototype trong javasript nhé.

Mình sẽ sử dụng Number Constructor để tạo ra một con số, sau đó in ra xem nó có những gì nhé.

let number = new Number(12);
console.log(number);

javascript prototype 2 JPG

Như bạn thấy trong hình, đối tượng number không tồn tại một thuộc tính nào cả, nhưng prototype của nó lại có rất nhiều phải không nào?

Bây giờ mình sẽ sử dụng phương thức toString để chuyển đổi number này thành một chuỗi.

let number = new Number(12);
let string = number.toString();

console.log(typeof string); // String

Vì phương thức toString không tồn tại trong number object nên nó sẽ tìm trong prototype, và trong prototype có phương thức toString nên nó sử dụng luôn, kết quả là ta đã chuyển được number sang chuỗi string.

Bây giờ mình sẽ định nghĩa một phương thức toString vào đối tượng bằng cách sau:

let number = new Number(12);
number.toString = function(){
    console.log("Bạn đang gọi đến phương thức number của object");
};

console.log(number);

Đoạn code này sẽ in ra kết quả như sau:

javascript prototype 3 JPG

Như bạn thấy, ta có cả hai phương thức toString, một là của đối tượng, một là nằm trong prototype của đối tượng. Nếu như xét về độ ưu tiên khi gọi đến phương thức toString thì javascript sẽ sử dụng cái đầu tiên. Ta hãy thử xem ngay bây giờ nhé.

let number = new Number(12);
number.toString = function(){
    console.log("Bạn đang gọi đến phương thức number của object");
};

// Kết quả: Bạn đang gọi đến phương thức number của object
var string = number.toString();

console.log(typeof string); // Undefined

Kết quả đúng như ta mong đợi.

javascript prototype 4 JPG

Lưu ý: Đây là quy tắc quan trọng và mọi đối tượng đều áp dụng quy tắc này nhé các bạn.

3. Prototype chain trong Javascript

Prototype chain là một thuật ngữ về quá trình truy xuất đến các thuộc tính giữa các lớp kế thừa với nhau.

Nếu bạn chưa biết khái niệm kế thừa thì xem trong bài viết extends trong javascript nhé.

Prototype của lớp không có kế thừa.

Giả sử mình có lớp Developer như sau:

class Developer  {
  constructor(name){
      this.name = name;
  }
  code (thing) {
    console.log(`${this.firstName} coded ${thing}`)
  }
}

Bây giờ mình tạo mới một instance của đối tượng Developer, và sau đó console.log xem nó có gì.

const cuong = new Developer('Cuong')
console.log(cuong);

Kết quả:

javascript chain prototype 2 JPG

Giải thích:

  • Instance cuong thuộc đối tượng Developer,
  • Nó không có thuộc tính nào cả,
  • Nó prototype và trong prototype này có một function code.
  • Trong prototype lại có thêm một prototype khác, và đó là một object mặc định của javascript. Nếu lớp Developer có kế thừa từ lớp A thì object này sẽ được thay thế bằng lớp A đó.

Prototype của lớp có kế thừa

Giả sử mình sẽ định nghĩa một lớp Human, sau đó tạo thêm một lớp Developer kế thừa từ lớp Human.

  • Lớp Human có phương thức sayHello.
  • Lớp Developer có phương thức code.

Đây là code cho lớp Human.

class Human {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastname = lastName
  }

  sayHello () {
    console.log(`Hi, I'm ${this.firstName}`)
  }
}

Và đây là code cho lớp Developer.

class Developer extends Human {
  code (thing) {
    console.log(`${this.firstName} coded ${thing}`)
  }
}

Vì lớp Developer được kế thừa từ lớp Human nên nó tồn tại hai phương thức sayHellocode.

const cuong = new Developer('Cuong', 'Nguyen')
cuong.sayHello() // Hi, I'm Cuong
cuong.code('website') // Cuong coded website

Thử console.log xem có gì trong đối tượng cuong này nhé.

console.log(cuong);

javascript chain prototype 1 JPG

Giải thích kết quả như sau: Instance cuong sẽ có ..

  • Đây là một đối tượng Developer, có hai thuộc tính là firstNamelastName.
  • Có prototype, trong prototype này có:
    • Một hàm tên là code.
    • Một prototype khác trỏ tới lớp Human.
      • Prototype này có hàm sayHello

Khi bạn gọi một thuộc tính thì nó sẽ tìm lần lượt theo thứ tự như trong hình sau:

javascript chain prototype 3 JPG

Nó sẽ áp dụng quy tắc mà chúng ta đã học ở phần 2:

  • Đầu tiên là tìm trong thuộc tính của đối tượng
  • Tiếp theo tìm trong prototype của đối tượng
  • Nếu trong prototype p1 có thêm một prototype (p2) khác nữa thì nó sẽ tiếp tục áp dụng quy tắc để tìm kiếm trên p2 này.

4. Có nên sử dụng prototype trong Javascript?

Mỗi đối tượng trong javascript được chia làm hai phần, thứ nhất là thuộc tính của đối tượng, thứ hai là prototype của đối tượng.

Khi bổ sung một thuộc tính cho đối tượng thì bạn có thể chọn một trong hai cách trên, cụ thể như sau:

let domain = new String("freetuts.net");
domain.show = function(){
    // Code
};

// Hoặc
String.prototype.show = function(){
    // code
};

Tuy nhiên, mỗi cách lại có công dụng khác nhau, và tùy thuộc vào từng trường hợp mà bạn chọn cách cho phù hợp.

Trường hợp 1: Thêm trực tiếp vào đối tượng

Cách này thì thuộc tính thêm sử dụng được trong instance mà bạn đã thêm.

let domain_1 = new String("freetuts.net");
domain_1.show = function(){
    console.log(this.valueOf());
};

// Kết quả: freetuts.net
domain_1.show();


let domain_2 = new String("techtuts.net");

// Kết quả: Lỗi do domain_2 không tồn tại method show
domain_2.show();

Trường hợp 2: Thêm vào prototype

Cách này thì thuộc tính thêm có thể sử dụng trong mọi instance.

String.prototype.show = function(){
    console.log(this.valueOf());
};

let domain_1 = new String("freetuts.net");
domain_1.show(); // Kết quả: freetuts.net

let domain_2 = new String("techtuts.net");
domain_2.show(); // Kết quả: techtuts.net

Lời kết: Có một số mẫu test đã chứng minh răng khi sử dụng prototype thì sẽ cho tốc độ nhanh hơn. Tuy nhiên, chỉ trường hợp bạn chạy trên 1 triệu toán tử thì mới ảnh hưởng. Vì vậy, việc áp dụng prototype hay không là không quan trọng.

Như vậy là chúng ta đã tìm hiểu xong phần prototype trong Javascript. Đây là kiến thức rất khó và quan trọng, nó giúp bạn hiểu làm việc với các đối tượng dễ dàng hơn. Nếu thấy bài viết hay thì hãy chia sẻ đến mọi người nhé.

Cùng chuyên mục:

Cách gộp hai object javascript lại với nhau

Cách gộp hai object javascript lại với nhau

Cách lấy chiều dài của object trong Javascript

Cách lấy chiều dài của object trong Javascript

Hướng dẫn giải phương trình bậc 1 bằng Javascript

Hướng dẫn giải phương trình bậc 1 bằng Javascript

Cách dùng nextSibling trong javascript

Cách dùng nextSibling trong javascript

Cách dùng insertAdjacentHTML trong javascript

Cách dùng insertAdjacentHTML trong javascript

Cách dùng innerHTML trong Javascript

Cách dùng innerHTML trong Javascript

Cách dùng insertBefore trong javascript

Cách dùng insertBefore trong javascript

Cách dùng insertAfter trong Javascript

Cách dùng insertAfter trong Javascript

Cách dùng parentNode trong Javascript

Cách dùng parentNode trong Javascript

Cách dùng parentElement trong Javascript

Cách dùng parentElement trong Javascript

Tính tổng các phần tử trong mảng javascript

Tính tổng các phần tử trong mảng javascript

Tính tổng hai số bằng Javascript (cộng hai số)

Tính tổng hai số bằng Javascript (cộng hai số)

Cách gán giá trị cho thẻ input trong javascript

Cách gán giá trị cho thẻ input trong javascript

Để gán giá trị cho thẻ input thì ta có hai cách, thứ nhất là…

Cách kiểm tra số nguyên âm trong javascript

Cách kiểm tra số nguyên âm trong javascript

Cách kiểm tra số nguyên dương trong javascript

Cách kiểm tra số nguyên dương trong javascript

Hàm closure trong javascript

Hàm closure trong javascript

Closure là một khái niệm không phải ai cũng ..

Biểu thức chính quy RegEx trong Javascript

Biểu thức chính quy RegEx trong Javascript

Bài này chúng ta sẽ tìm hiểu đến chuỗi và cách sử dụng biểu thức…

Cách dùng Import / Export Module trong javascript

Cách dùng Import / Export Module trong javascript

Khi bạn xây dựng một ứng dụng nhỏ thì việc đặt

Cơ chế hoạt động của hoisting trong Javascript

Cơ chế hoạt động của hoisting trong Javascript

Hoisting là vấn đề liên quan đến cách khai báo biến trong Javascript. Nó liên…

Cấp độ private / protected của class trong Javascript

Cấp độ private / protected của class trong Javascript

Top