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
  • Từ lâu rồi suy nghĩ làm thế nào để tăng sự hiện diện thương hiệu, cũng như người dùng cho blog. Nghĩ đi nghĩ lại thì chỉ có cách chia sẻ lên mạng xã hội hoặc trông chờ họ tìm kiếm, cho đến khi...

    In cái áo này được cái tắc đường khỏi phải lăn tăn, càng đông càng vui vì hàng trăm con mắt nhìn thấy cơ mà 🤓

    (Có tác dụng thật nha 🤭)

    » Xem thêm
  • Một vòng của sự phát triển nhiều dự án khá là thú vị. Tóm tắt lại trong 3 bước: Thấy một cái gì đó phức tạp -> Làm cho nó đơn giản đi -> Thêm thắt tính năng cho đến khi nó phức tạp... -> Quay trở lại vòng lặp mới.

    Tại sao lại như vậy? Để mình lấy 2 ví dụ cho các bạn thấy.

    Markdown ra đời với mục tiêu tạo ra một định dạng văn bản thô "dễ viết, dễ đọc, dễ dàng chuyển thành một dạng gì đó như HTML". Vì thời đó chẳng ai đủ kiên nhẫn mà vừa ngồi viết vừa thêm định dạng cho văn bản hiển thị ở trên web như thế nào. Ấy vậy mà giờ đây người ta đang "nhồi nhét" hoặc tạo ra các biến thể dựa trên markdown để bổ sung thêm nhiều định dạng mới đến mức... chẳng nhớ nổi hết cú pháp.

    React cũng là một ví dụ. Từ thời PHP, việc khát khao tạo ra một cái gì đó tách biệt hẳn giao diện người dùng và phần xử lý logic chính của ứng dụng thành 2 phần riêng biệt cho dễ đọc, dễ viết. Kết quả là các thư viện UI/UX phát triển rất mạnh mẽ, mang lại khả năng tương tác với người dùng rất tốt, còn phần logic ứng dụng thì nằm ở một máy chủ riêng biệt. Bộ đôi Front-end, Back-end cũng từ đấy mà thịnh hành, không thể thiếu anh bồi bàn REST API. Ấy vậy mà giờ đây React trông cũng không khác biệt gì so với PHP là mấy, kéo theo là cả Vue, Svelte... lại cùng quy tất cả về một mối.

    Cơ mà không phải vòng lặp là xấu, ngược lại vòng lặp này mang tính tiến hoá nhiều hơn là "cải lùi". Nhiều khi lại tạo ra được cái hay hơi cái cũ thế là người ta lại dựa trên cái hay đó để tiếp tục lặp. Nói cách khác là chắc lọc tinh hoa từng tí một tí một á 😁

    » Xem thêm
  • Song song với các dự án chính thức thì thi thoảng mình vẫn thấy các dự án "bên lề" nhằm tối ưu hoặc cải tiến ngôn ngữ theo khía cạnh nào đó. Ví dụ nature-lang/nature là một dự án hướng tới cải tiến Go, mang lại một số thay đổi nhằm giúp cho việc sử dụng Go trở nên thân thiện hơn.

    Nhìn lại mới thấy hao hao JavaScript 😆

    » 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...