Nội dung chính
Đối với một ngôn ngữ prototype-based như JavaScript, prototype là một khái niệm cốt lõi mà bất cứ lập trình viên JavaScript nào cũng cần biết. Hãy cùng đi qua những kiến thức từ cơ bản như prototype là gì cho đến cần thiết nhất về cách sử dụng prototype và áp dụng chúng hiệu quả vào dự án của bạn.
Đọc bài viết này bạn sẽ dễ dàng nắm được:
- Prototype là gì trong JavaScript và những thuật ngữ cơ bản
- Hiểu rõ cơ chế và các thao tác cần thiết để sử dụng prototype hiệu quả
- Nắm rõ kiến thức về tính kế thừa của prototype
Trước khi đọc bài viết này, bạn nên đọc bài viết Tổng quan JavaScript A-Z để nắm rõ kiến thức cơ bản nhất về ngôn ngữ lập trình này.
Việc làm JavaScript toàn quốc trên ITviec
Prototype là gì?
Prototype là gì?
Với JavaScript, prototype dùng để chỉ một cơ chế cho phép các object kế thừa các thuộc tính (properties) và phương thức (methods) từ các object khác.
Tham khảo: Học JavaScript với 20+ tài liệu JavaScript “chất” nhất
Theo đó, bạn sẽ gặp 2 khái niệm là prototype của hàm và prototype của object. Định nghĩa cụ thể của hai khái niệm đó như sau:
- Prototype của hàm: Prototype của hàm là object instance mà sẽ trở thành prototype cho tất cả các object được tạo bằng cách sử dụng hàm này làm constructor.
- Prototype của object: Prototype của object là object instance mà object được kế thừa từ đó.
Có thể nói, prototype là một đối tượng được liên kết với mọi hàm và object theo mặc định trong JavaScript. Trong đó, bạn có thể truy cập và sửa đổi được thuộc tính prototype của hàm. Còn thuộc tính prototype của object (còn gọi là attribute) sẽ không được hiển thị.
Prototype của hàm là gì?
Ảnh mẫu: @TutorialsTeacher.com
Trong JavaScript, tất cả các hàm đều có thuộc tính prototype. Thuộc tính prototype đó tương tự như một class definition trong các ngôn ngữ hướng đối tượng khác, nhưng để định nghĩa chính xác hơn, prototype của hàm là một object instance.
Mọi hàm trong JavaScript đều có một prototype object cho dù bạn có sử dụng hay không.
Sau khi bạn tạo một object mới bằng cách sử dụng từ khóa new, chuyển object mới dưới dạng this tới hàm constructor. Sau đó, hàm constructor lấy object instance (mà được trỏ tới bởi thuộc tính prototype của hàm đó) và gán object instance làm prototype cho object đó.
Prototype của object là gì?
Object không có thuộc tính prototype, nhưng chúng có prototype.
Prototype của một object là một object mà kế thừa các thuộc tính từ object đó. Lưu ý, kế thừa thuộc tính, không phải kế thừa hàm hay class. Điều này khác với prototype của hàm vì prototype của hàm được sử dụng làm object để được chỉ định làm prototype cho các object mới mà chúng được tạo bằng cách sử dụng hàm này làm hàm tạo.
Tuy khác nhau về mặt khái niệm nhưng trên thực tế, prototype của một hàm và prototype của một object thường trỏ đến cùng một object trong bộ nhớ.
Có thể truy xuất prototype của một object bằng cách gọi myObject.__proto__
trong hầu hết các trình duyệt và gọi Object.getPrototypeOf(myObject)
trong tất cả các trình duyệt.
[[Prototype]] là gì?
Trong JavaScript, các object đều có một thuộc tính [[Prototype]]
ẩn, mà có thể là null
hoặc là tham chiếu object khác. Object được tham chiếu bởi [[Prototype]]
được gọi là một “prototype”.
Chúng ta có thể sử dụng obj .__ proto__
để truy cập vào [[Prototype]]
. Đây là một getter/setter đã cũ và có những cách khác mà sẽ được đề cập ngay trong bài viết này.
__proto__ là gì?
__proto__
không giống như thuộc tính [[Prototype]]
. Đây là một getter/setter cho [[Prototype]]
nhưng đã lỗi thời.
JavaScript hiện đại khuyến khích lập trình viên nên sử dụng hàm Object.getPrototypeOf/Object.setPrototypeOf
thay vì get/set prototype.
Công dụng của prototype là gì?
Như đã trình bày, prototype cho phép bạn dễ dàng xác định các phương thức cho tất cả các instance của một object cụ thể. Cái hay là phương thức được áp dụng cho prototype, vì vậy chúng chỉ được lưu trong bộ nhớ một lần, nhưng mọi hành động của object đều có quyền truy cập vào.
Hãy xem qua object Pet trong ví dụ bên dưới:
function Pet(name, species){ this.name = name; this.species = species; } function view(){ return this.name + " is a " + this.species + "!"; } Pet.prototype.view = view; var pet1 = new Pet('Tom', 'Cat'); alert(pet1.view()); //Đầu ra: "Tom is a Cat!"
Từ ví dụ trên, bằng cách sử dụng prototype đơn giản, bạn đã đảm bảo rằng tất cả các object Pet đều có quyền truy cập vào phương thức xem.
Bạn có thể sử dụng phương pháp prototype để làm việc hiệu quả hơn. Ví dụ:
Giả sử chúng ta muốn có một object Cat. Object này sẽ kế thừa từng phương thức và thuộc tính được sử dụng trong object Pet và đồng thời cũng có thể tạo một chức năng đặc biệt mà chỉ object Cat mới có quyền truy cập.
Prototype sẽ giúp bạn triển khai một cách dễ dàng.
Lợi ích của prototype là gì?
Cơ chế prototype cho phép tái sử dụng code và tạo thành cơ sở kế thừa object trong JavaScript. Điều này không chỉ làm giảm sự dư thừa mà còn góp phần quản lý bộ nhớ hiệu quả hơn vì các chức năng được chia sẻ không được sao chép cho từng instance của object. Cụ thể hơn:
Kế thừa object và khả năng tái sử dụng code
Prototype là nền tảng để thực hiện kế thừa object trong JavaScript. Khi tạo các object, JavaScript sẽ tự động liên kết các object với prototype của chúng, tạo thành một prototype chain.
Đọc thêm: Prototype chain là gì? Tính kế thừa với prototype chain hoạt động như thế nào?
Chuỗi này cho phép các object kế thừa các thuộc tính và phương thức từ prototype của chúng, tạo ra mối quan hệ phân cấp. Khả năng kế thừa các chức năng (functionality) thúc đẩy khả năng tái sử dụng code, khi bạn xác định các thuộc tính chung trong prototype.
Bằng cách tận dụng các prototype, bạn có thể code gọn gàng hơn, có tổ chức hơn. Đây là một tính năng mạnh mẽ cho phép các lập trình viên xây dựng các ứng dụng phức tạp và có thể mở rộng ứng dụng một cách hiệu quả.
Quản lý bộ nhớ hiệu quả
Tối ưu hóa bộ nhớ rất quan trọng đối với các ứng dụng hiệu suất cao, đặc biệt là những ứng dụng xử lý các tập dữ liệu lớn. Prototype đóng một vai trò quan trọng trong việc quản lý bộ nhớ bằng cách giảm mức tiêu thụ bộ nhớ.
Khi bạn thêm các thuộc tính và phương thức vào prototype của một object, chúng sẽ được chia sẻ giữa tất cả các instance của object đó, thay vì bị trùng lặp trong mỗi instance. Do đó, các instance chỉ cần lưu trữ dữ liệu duy nhất của chúng, dẫn đến một thiết kế tiết kiệm bộ nhớ hơn.
Ưu điểm này mang lại giá trị rõ rệt khi bạn phải xử lý một số lượng đáng kể các instance hoặc xử lý các tác vụ sử dụng nhiều tài nguyên. Do đó, prototype sẽ giúp bạn cải thiện đáng kể hiệu suất ứng dụng và đảm bảo trải nghiệm người dùng mượt mà hơn.
Tối ưu hóa hiệu suất
Prototype đóng một vai trò quan trọng trong việc tối ưu hóa hiệu suất trong các ứng dụng JavaScript.
Khi bạn xác định các phương thức và thuộc tính được chia sẻ trong prototype, JavaScript có thể truy cập chúng một cách hiệu quả. Thay vì tìm kiếm thuộc tính trong từng instance riêng lẻ, JavaScript đi theo prototype chain để tìm chức năng được chia sẻ, dẫn đến thời gian truy cập nhanh hơn.
Việc tối ưu hóa này ngày càng trở nên quan trọng trong các ứng dụng quan trọng về hiệu suất, chẳng hạn như trò chơi thời gian thực hoặc các ứng dụng sử dụng nhiều dữ liệu.
Việc làm JavaScript ở TP. HCM trên ITviec
Việc làm JavaScript ở Hà Nội trên ITviec
Cách tạo prototype
Cùng nhắc lại object prototype là gì: Object prototype là một thuộc tính ẩn và có thể được truy cập bằng phương thức Object.getPrototypeOf()
hoặc thuộc tính __proto__
.
Về cơ bản, đây là một phương pháp chi tiết để tạo các instance mới của một object và cho phép các object kế thừa các thuộc tính và phương thức từ prototype của chúng.
Ví dụ 1: Tạo và truy cập prototype
// Tạo một object với 1 prototype const personPrototype = { greet: function() { return `Hello, my name is ${this.name} and I'm ${this.age} years old.`; }, }; const person1 = Object.create(personPrototype); person1.name = "John"; person1.age = 30; console.log(person1.greet()); // Đầu ra: "Hello, my name is John and I'm 30 years old." const person2 = Object.create(personPrototype); person2.name = "Alice"; person2.age = 25; console.log(person2.greet()); // Đầu ra: "Hello, my name is Alice and I'm 25 years old."
Ví dụ 2: Thêm phương thức vào prototype
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.greet = function() { return `Hello, my name is ${this.name} and I'm ${this.age} years old.`; }; const person1 = new Person("John", 30); console.log(person1.greet()); // Đầu ra: "Hello, my name is John and I'm 30 years old."
Trong ví dụ thứ hai, chúng ta định nghĩa một hàm xây dựng (constructor function) Person và thêm một phương thức chào (greet method) vào prototype của chúng. Khi chúng ta tạo một instance mới của Person bằng cách sử dụng từ khóa new, instance này sẽ tự động liên kết với Person.prototype, cho phép truy cập phương thức chào thông qua prototype chain.
Cách sử dụng và thao tác với prototype
1. Sử dụng constructor và từ khóa new
Trong JavaScript, bạn có thể sử dụng các prototype kết hợp với các hàm constructor và từ khóa new để triển khai lập trình hướng đối tượng (OOP) và tạo các object có chức năng dùng chung.
Từ khóa new được sử dụng để khởi tạo các object từ các hàm constructor và các prototype được sử dụng để xác định các thuộc tính và phương thức dùng chung cho các object đó. Hãy xem qua các bước để sử dụng prototype với hàm constructor và từ khóa new:
- Bước 1: Xác định Hàm Constructor
Đầu tiên, bạn cần định nghĩa một hàm constructor. Hàm này hoạt động như một kế hoạch chi tiết để tạo các object có các thuộc tính và phương thức tương tự.
Bên trong hàm constructor, bạn có thể thiết lập các thuộc tính object bằng cách sử dụng từ khóa this.
function Person(name, age) { this.name = name; this.age = age; }
- Bước 2: Thêm các phương thức dùng chung vào prototype
Tiếp theo, bạn có thể mở rộng prototype của hàm này để thêm các phương thức dùng chung (shared method) mà tất cả các instance sẽ kế thừa. Bằng cách sửa đổi prototype, bạn đảm bảo rằng mỗi object mới được tạo từ hàm constructor sẽ chia sẻ các phương thức này, tránh trùng lặp không cần thiết.
Person.prototype.sayHello = function() { return `Hello, my name is ${this.name}, and I am ${this.age} years old.`; };
- Bước 3: Tạo Object Instances với từ khóa new
Bây giờ, bạn có thể tạo các instance của object bằng cách sử dụng từ khóa new và hàm constructor. Khi sử dụng new, một object trống mới được tạo và gán cho this, cho phép hàm constructor thiết lập các thuộc tính và phương thức trên object mới được tạo.
const person1 = new Person("John", 30); const person2 = new Person("Alice", 25); console.log(person1.sayHello()); // Đầu ra: "Hello, my name is John, and I am 30 years old." console.log(person2.sayHello()); // Đầu ra: "Hello, my name is Alice, and I am 25 years old."
- Bước 4: Kế thừa object thông qua prototype
Nhờ các prototype, cả person1 và person2 đều kế thừa phương thức sayHello từ Person.prototype. Các instance này chia sẻ cùng một phương thức, nhưng chúng có thể chứa dữ liệu duy nhất (ví dụ: tên và tuổi khác nhau) mà không cần sao chép chính phương thức đó.
Bằng cách sử dụng các prototype với hàm constructor và từ khóa new, bạn có thể tạo và quản lý hiệu quả các instance của object với các thuộc tính và phương thức dùng chung, thúc đẩy khả năng tái sử dụng code và hiệu suất bộ nhớ trong các ứng dụng JavaScript của bạn.
2. Thay đổi prototype
Trong JavaScript, bạn có thể thay đổi prototype của một hàm constructor object để sửa đổi tất cả các instance kế thừa. Thay đổi prototype cho phép bạn thêm các thuộc tính hoặc phương thức mới hoặc sửa đổi các thuộc tính hoặc phương thức hiện có một cách linh hoạt trong thời gian chạy theo các cách sau:
- Sửa đổi prototype hiện có:
Nếu bạn muốn thêm một phương thức hoặc thuộc tính mới vào prototype hiện có, bạn có thể thực hiện trực tiếp bằng cách gán cho prototype.
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHello = function() { return `Hello, my name is ${this.name}, and I am ${this.age} years old.`; }; const person1 = new Person("John", 30); console.log(person1.sayHello()); // Đầu ra: "Hello, my name is John, and I am 30 years old." // Chỉnh sửa prototype đã tồn tại Person.prototype.sayGoodbye = function() { return `Goodbye from ${this.name}!`; }; console.log(person1.sayGoodbye()); // Đầu ra: "Goodbye from John!"
- Thay thế toàn bộ prototype:
Nếu bạn muốn thay thế toàn bộ prototype bằng một object mới, bạn có thể làm như vậy bằng cách sử dụng phương thức Object.create(). Phương thức này cho phép bạn tạo một object mới và đặt object đó làm prototype của hàm constructor.
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHello = function() { return `Hello, my name is ${this.name}, and I am ${this.age} years old.`; }; const person1 = new Person("John", 30); console.log(person1.sayHello()); // Đầu ra: "Hello, my name is John, and I am 30 years old." // Thay thế toàn bộ prototype const newPrototype = { sayGoodbye() { return `Goodbye from ${this.name}!`; }, }; Person.prototype = newPrototype; console.log(person1.sayGoodbye()); // Đầu ra: "Goodbye from John!"
Mối quan hệ giữa tính kế thừa và prototype là gì?
Khi nói đến tính kế thừa, JavaScript chỉ có một cấu trúc: object. Mỗi object có một thuộc tính riêng chứa liên kết đến một object khác được gọi là prototype. Prototype object đó lại có một prototype riêng, v.v. cho đến khi có một object mà prototype của nó là null
.
Theo định nghĩa, null
không có prototype và đóng vai trò là liên kết cuối cùng trong prototype chain này. Có thể thay đổi bất kỳ thành viên nào của prototype chain hoặc thậm chí hoán đổi prototype trong thời gian chạy, do đó, các khái niệm như static dispatching không tồn tại trong JavaScript.
Mặc dù việc này thường được coi là một trong những điểm yếu của JavaScript, nhưng trên thực tế, bản thân mô hình kế thừa theo prototype lại mạnh hơn những mô hình kế thừa cổ điển thường thấy ở những ngôn ngữ khác. Ví dụ, việc xây dựng một mô hình cổ điển trên một mô hình prototype là khá đơn giản.
Mặc dù các class hiện đã được áp dụng rộng rãi và đã trở thành một mô hình (model) mới trong JavaScript, nhưng các class không mang lại một pattern kế thừa mới. Trong khi các class lại trừu tượng hóa hầu hết cơ chế prototype, thì việc hiểu cách prototype hoạt động như thế nào vẫn có những lợi ích nhất định trong việc triển khai.
Những câu hỏi thường gặp về prototype trong Javascript
1. Prototype cho phép kế thừa object như thế nào?
Prototype cho phép kế thừa object bằng cách cho phép các object chia sẻ các thuộc tính và phương thức với các prototype của chúng.
Khi một thuộc tính hoặc phương thức được truy cập trên một object, trước tiên, JavaScript sẽ tìm trong chính object đó. Nếu không tìm thấy, hệ thống tiếp tục tìm kiếm trong prototype của object, và tiếp tục như vậy trong prototype chain.
2. Làm thế nào để bạn tạo một prototype trong JavaScript?
Bạn có thể tạo một prototype bằng cách xác định một object hoặc hàm constructor và thêm các thuộc tính và phương thức vào prototype của chúng.
Đối với các hàm constructor, bạn có thể sử dụng thuộc tính prototype để xác định các chức năng được chia sẻ.
3. Prototype cải thiện khả năng tái sử dụng code như thế nào?
Bằng cách xác định các chức năng được chia sẻ trong prototype, bạn có thể tạo một kế hoạch chi tiết cho nhiều instance để kế thừa từ đó. Điều này làm giảm sự dư thừa và thúc đẩy khả năng tái sử dụng code, làm cho codebase có tổ chức và dễ bảo trì hơn.
4. Mối quan hệ giữa hàm constructor và prototype là gì?
Trong JavaScript, mọi hàm constructor đều có thuộc tính prototype trỏ đến object prototype. Khi bạn tạo một object bằng hàm constructor với từ khóa new, thuộc tính __proto__
của object sẽ liên kết với prototype của hàm constructor.
5. Bạn có thể thay đổi prototype của một object sau khi tạo không?
Có, bạn có thể sửa đổi prototype của một object trong thời gian chạy. Những thay đổi được thực hiện đối với prototype sẽ được phản ánh trong tất cả các instance kế thừa từ prototype đó.
6. Prototype cải thiện hiệu suất như thế nào?
Prototype cải thiện hiệu suất bằng cách cho phép truy cập các phương thức dùng chung thông qua prototype chain. Điều này làm giảm thời gian tra cứu thuộc tính và dẫn đến tốc độ thực thi code tốt hơn.
Tổng kết
Prototype là một khái niệm quan trọng trong JavaScript, cho phép chúng ta tạo các mẫu (templates) và chia sẻ các thuộc tính và phương thức chung giữa các object. Hiểu prototype là gì và áp dụng chúng một cách chính xác giúp tối ưu hóa việc sử dụng bộ nhớ, tái sử dụng code và xây dựng các ứng dụng JavaScript linh hoạt và hiệu quả.
Bạn thấy bài viết hay và hữu ích? Đừng ngại Share với bạn bè và đồng nghiệp nhé.
Và nhanh tay tham khảo việc làm IT “chất” trên ITviec!