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
  • Rust sinh ra để tái định nghĩa nhiều thứ. Trong đó chắc phải kể đến JavaScript. Từ đầu năm đến giờ các công cụ làm từ Rust mà để cho JavaScript dùng đếm sương sương cũng vài ba cái rồi. Mới đây nhất là Oxc.

    Oxc là một công cụ phân tích cú pháp (parser), kiểm tra lỗi (lint), định dạng (formatter), chuyển đổi (transformer), minifier... tất cả đều được viết bằng Rust, trong một công cụ duy nhất.

    Mặc dù vẫn đang trong quá trình xây dựng nhưng thử nhìn điểm hiệu năng của nó so với swc hoặc eslint mà xem 🫣

    » Xem thêm
  • Mấy hôm nay, à mà cũng không hẳn, do sự kiện WWDC vừa rồi nên Apple lại bị dân cư mạng mang ra bàn tán rằng rốt cục thì các tính năng AI của họ đang ở đâu? Trong khi các hãng khác đang lao mình vào việc mang AI lên thiết bị, phần mềm của họ thì Apple lại đang có vẻ... không quan tâm lắm.

    Thậm chí mới đây các nhà nghiên cứu của Apple cho rằng các mô hình LLM sẽ "sụp đổ hoàn toàn về độ chính xác" khi được đưa ra các vấn đề cực kỳ phức tạp. Chỉ ra rằng suy luận chỉ là huyễn hoặc thì ngay lập tức đã có nhiều bài phản bác nghiên cứu này. Một lần nữa cho thấy rằng Apple đang suy nghĩ điều gì với AI trên thiết bị của họ?

    Mình thì nghĩ đơn giản thôi, Apple có vẻ đang gặp khó khăn với việc tạo ra AI cho riêng họ. Tức là khó khăn ngay từ đoạn thu thập dữ liệu để đào tạo rồi. Họ luôn tỏ ra tôn trọng quyền riêng tư của người dùng nên chẳng lẽ lại lên mạng đi xào nấu dữ liệu ở khắp nơi, hoặc "chôm" dữ liệu dưới máy người dùng lên? Chắc chắn, họ cũng không muốn cung cấp thêm dữ liệu người dùng cho các bên thứ 3 như OpenAI.

    Nhưng nhờ những khó khăn này biết đâu họ lại tìm ra được hướng đi mới. Ai cũng chọn phần dễ thì gian khổ để phần cho ai 😁. À mình không phải là "fan" của Apple, chỉ là thấy cái nào phù hợp thì dùng thôi 🤓

    » Xem thêm
  • Người "nhạy cảm" với markdown đó là khi thấy một thư viện tạo khung soạn thảo mới là nhảy ngay vào xem nó có gì mới. Milkdown/milkdown là một ví dụ.

    Xem thử thì thấy ổn phết mọi người ạ. Vài nữa thử tích hợp vào opennotas xem sao. Mang tiếng là ứng dụng ghi chú hỗ trợ markdown cơ mà cái thư viện tiptap nó không chịu làm thêm phần hỗ trợ markdown 😩. Dùng thư viện ngoài thì vẫn chưa ngon cho lắ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

5 bài học sâu sắc

Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? Hãy bấm vào ngay!

Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? 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...