Prototypes trong JavaScript là gì? Tại sao nó lại quan trọng?

Prototypes trong JavaScript là gì? Tại sao nó lại quan trọng?

Tin ngắn hàng ngày dành cho bạn
  • Không hề kém cạnh, Google mới đây đã giới thiệu Gemini CLI - Một dạng AI Agent tương tự như Codex hay Claude Code.

    Điều đáng lưu ý là họ cho dùng miễn phí tới... 1000 truy vấn mỗi ngày. Nhiều đấy chứ. Ngoài ra họ cũng mã nguồn mở dự án này để đảm bảo tính minh bạch, học tập và nghiên cứu 🤓

    » Xem thêm
  • Lại có thêm một công cụ hỗ trợ tìm kiếm nhanh lịch sử gõ lệnh nè mọi người: atuinsh/atuin.

    Điều thú vị là nó dùng SQLite để lưu trữ. Ngoài ra còn cung cấp tính năng đồng bộ hóa (mã hóa) hoàn toàn lịch sử giữa các máy với nhau nữa. Hay ghê 🤓

    » Xem thêm
  • Mình thấy ấn tượng với mô hình gemma-3n-E4B của nhà Google ghê. Đây là một trong những mô hình hứa hẹn mang các mô hình ngôn ngữ lớn xuống chạy trên thiết bị di dộng hoặc web hoặc nhúng (embedded)...

    Cảm giác nó hiểu lời nhắc hơn á, tại vì mình thử nhiều mô hình ít tham số mà nó hay lơ đi lời nhắc của mình. Ví dụ bảo: "Chỉ trả về câu trả lời, không cần giải thích gì thêm" thì rất nhiều cái vẫn cứ phải chêm vào câu mở đầu, giải thích... còn với gemma-3n thì trả lời rất đúng trọng tâm.

    » Xem thêm

Vấn đề

Nếu đã quen với một ngôn ngữ lập trình hướng đối tượng từ trước, class là cú pháp rõ ràng nhất để nhận biết cách khai báo một đối tượng mới. JavaScript cũng được coi là một ngôn ngữ lập trình hướng đối tượng, nhưng nếu là một lập trình viên JS đời đầu, cỡ cách đây khoảng chục năm về trước thì JavaScript không hề có cú pháp class, thay vào đó nó hỗ trợ kế thừa thông qua prototype. Cách viết này có phần khác biệt với cú pháp của OOP trong đa số ngôn ngữ lập trình hướng đối tượng, nó cũng không được đánh giá cao về khả năng hỗ trợ nhiều tính năng mạnh mẽ của OOP.

Vài năm trở lại đây, ES6 ra đời mang đến từ khóa class để giúp lập trình viên dễ dàng viết mã theo hướng OOP hơn, hay nói cách khác là thân thiện hơn cho ai đang quen với cú pháp của OOP. Nhưng bản chất đằng sau nó vẫn là sự có mặt của prototype. Bài viết ngày hôm nay chúng ta sẽ cùng nhau tìm hiểu xem prototype là gì mà tại sao nó lại quan trọng trong JavaScript nhé.

Prototypes là gì?

Prototypes là cơ chế mà các đối tượng JavaScript kế thừa các chức năng từ nhau. Mỗi đối tượng được tạo ra từ một hàm tạo đều có một prototype mặc định, đó là prototype của hàm tạo đó.

Mọi đối tượng trong JavaScript đều có các thuộc tính, phương thức được tích hợp sẵn gọi là prototype. Bản thân prototype cũng là một đối tượng, vì vậy prototype sẽ có prototype riêng, tạo nên một chuỗi prototype. Chuỗi này chỉ kết thúc khi prototype bằng null.

Prototypes định nghĩa các thuộc tính và phương thức mà mỗi đối tượng được tạo ra từ hàm tạo đó có thể truy cập được. Khi một đối tượng được tạo ra, nó sẽ kế thừa tất cả các thuộc tính và phương thức từ prototype của hàm tạo.

Ví dụ:

const myObject = {
  name: "2coffee",
  greet() {
    console.log(`Greetings from ${this.name}`);
  },
};

myObject.greet(); // Greetings from 2coffee

Trong ví dụ trên, greet() là một phương thức của myObject, do đó nó có thể gọi phương thức đó. Nhưng ngoài greet() ra, myObject là một Object nên nó kế thừa lại toàn bộ prototype của Object. Các prototype có trong myObject lúc này sẽ trông giống như là:

__defineGetter__
__defineSetter__
__lookupGetter__
__lookupSetter__
__proto__
name
constructor
greet
hasOwnProperty
isPrototypeOf
propertyIsEnumerable
toLocaleString
toString
valueOf

myObject có toàn quyền truy cập vào các prototype này, ví dụ như gọi phương thức toString.

myObject.toString(); // "[object Object]"

Khi cố gắng truy cập một thuộc tính của một đối tượng, nếu không thể tìm thấy thuộc tính trong chính đối tượng đó, nó sẽ chuyển sang tìm kiếm trong prototype của đối tượng cha. Cứ thế cho đến khi tìm thấy thuộc tính hoặc kết thúc chuỗi kế thừa mà trả về giá trị undefined.

Ví dụ với một đối tượng Date trong JavaScript. Date là một instance của Object, Date cũng có prototype riêng nên một đối tượng được tạo mới từ Date sẽ có ít nhất 3 lớp prototype.

prototype trong đối tượng Date

Sử dụng Prototypes để làm gì?

Như tôi đã nói ở trên, prototype sinh ra cho mục đích kế thừa như trong lập trình hướng đối tượng. Việc sử dụng prototype trong JavaScript cho phép ta thêm các thuộc tính và phương thức vào các đối tượng trong một cách hiệu quả hơn. Thay vì tạo các phương thức và thuộc tính cho từng đối tượng, ta có thể thêm chúng vào prototype của hàm tạo, do đó tất cả các đối tượng được tạo ra từ hàm tạo đó đều có thể truy cập đến chúng.

Ví dụ cho một chương trình khai báo lớp Geometry với hai phương thức calculatePerimeter (tính chu vi) và calculateArea (tính diện tích). Hai lớp SquareCircle kế thừa từ Geometry thông qua prototype được viết như sau:

// Khai báo lớp Geometry
function Geometry() {}

// Phương thức tính chu vi
Geometry.prototype.calculatePerimeter = function() { return; }

// Phương thức tính diện tích
Geometry.prototype.calculateArea = function() { return; }

// Khai báo lớp Square kế thừa từ Geometry
function Square(sideLength) {
  this.sideLength = sideLength;
}

Square.prototype = Object.create(Geometry.prototype);
Square.prototype.constructor = Square;

// Override phương thức tính chu vi cho Square
Square.prototype.calculatePerimeter = function() {
  return this.sideLength * 4;
}

// Override phương thức tính diện tích cho Square
Square.prototype.calculateArea = function() {
  return this.sideLength * this.sideLength;
}

// Khai báo lớp Circle kế thừa từ Geometry
function Circle(radius) {
  this.radius = radius;
}

Circle.prototype = Object.create(Geometry.prototype);
Circle.prototype.constructor = Circle;

// Override phương thức tính chu vi cho Circle
Circle.prototype.calculatePerimeter = function() {
  return 2 * Math.PI * this.radius;
}

// Override phương thức tính diện tích cho Circle
Circle.prototype.calculateArea = function() {
  return Math.PI * this.radius * this.radius;
}

Prototypes và Class

Kể từ ES6, JavaScript mang đến cho chúng ta cú pháp mới để kế thừa. Đó chính là từ khóa class, bằng cách sử dụng mới này, giúp chúng ta quen thuộc hơn với cú pháp giống như lập trình hướng đối tượng từ các ngôn ngữ khác.

Ví dụ cũng là chương trình trên nhưng viết lại bằng class mã sẽ trông gọn gàng hơn rất nhiều:

// Khai báo lớp Geometry
class Geometry {
  calculatePerimeter() { return; }

  calculateArea() { return; }
}

// Khai báo lớp Square kế thừa từ Geometry
class Square extends Geometry {
  constructor(sideLength) {
    super();
    this.sideLength = sideLength;
  }

  calculatePerimeter() {
    return this.sideLength * 4;
  }

  calculateArea() {
    return this.sideLength * this.sideLength;
  }
}

// Khai báo lớp Circle kế thừa từ Geometry
class Circle extends Geometry {
  constructor(radius) {
    super();
    this.radius = radius;
  }

  calculatePerimeter() {
    return 2 * Math.PI * this.radius;
  }

  calculateArea() {
    return Math.PI * this.radius * this.radius;
  }
}

class có từ ES6, tuy nhiên nó vẫn có nhiều hạn chế so với OOP trong các ngôn ngữ thuần hướng đối tượng khác như Java, C#... Một phần là do bản chất của class vẫn là dựa trên prototype, có điều nó tạo ra để giảm sự phức tạp của mã, giúp mã dễ đọc và gọn gàng hơn.

Tầm quan trọng của Prototypes

Prototypes là một khái niệm rất quan trọng trong JavaScript vì nó cho phép ta tạo ra các đối tượng với tính kế thừa và sử dụng lại mã nguồn một cách hiệu quả.

Các đối tượng được tạo ra từ cùng một hàm tạo đều có thể chia sẻ các thuộc tính và phương thức được định nghĩa trong prototype của hàm tạo đó. Điều này giúp giảm sự trùng lặp trong mã nguồn và tiết kiệm bộ nhớ cho ứng dụng.

Nếu để ý, bạn sẽ thấy gần như mọi thứ trong JavaScript đều xuất phát từ prototype. Tạo ra một object mới, nó sẽ kế thừa toàn bộ prototype từ Object, tạo ra một function, nó sẽ kế thừa toàn bộ prototype từ Function...

prototype của object

Ngoài ra, việc sử dụng prototype cũng cho phép ta tạo các đối tượng con kế thừa các thuộc tính và phương thức từ đối tượng cha. Điều này giúp tăng tính tái sử dụng trong mã nguồn, giảm thiểu sự trùng lặp và tăng tính bảo trì của ứng dụng.

Thêm vào đó, prototype cũng cung cấp một cách để mở rộng các đối tượng có sẵn trong JavaScript. Bằng cách thêm các thuộc tính và phương thức vào prototype của đối tượng, ta có thể mở rộng tính năng của đối tượng đó mà không cần thay đổi mã nguồn gốc.

Do đó, prototype là một khái niệm quan trọng trong JavaScript và là một trong những cách để tăng tính linh hoạt, hiệu quả và bảo trì sau này.

Tổng kết

Prototypes là cơ chế mà các đối tượng JavaScript kế thừa các chức năng từ nhau. Lập trình viên đời đầu phải viết mã kế thừa dựa trên prototype. Vài năm trở lại đây, ES6 ra đời mang đến cú pháp class giúp việc viết mã trở nên thân thiện hơn khi giờ đây các cú pháp của nó gần với ngôn ngữ hướng đối tượng hơn. Prototypes là một khái niệm quan trọng trong JavaScript, hầu như mọi thứ đều dựa trên prototype để hoạt động như Object, Function... vì thế hiểu rõ bản chất của Prototypes sẽ giúp cho việc học hỏi thêm kiến thức sau này.

Tài liệu tham khảo:

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (0)

Nội dung bình luận...