Object Prototypes trong JavaScript
Javascript nâng cao | by
Trong JavaScript, Prototype là một khái niệm cốt lõi giúp hiện thực hóa tính kế thừa mà không cần đến các lớp (class) như trong các ngôn ngữ lập trình hướng đối tượng truyền thống. Mọi object trong JavaScript đều có một Prototype, đóng vai trò như một khuôn mẫu chứa các thuộc tính và phương thức mà object có thể kế thừa.
Cơ chế Prototype-based Inheritance giúp JavaScript trở nên linh hoạt và mạnh mẽ, cho phép các object chia sẻ phương thức một cách hiệu quả mà không cần sao chép chúng. Việc hiểu rõ về Prototype, Prototype Chain, và cách mở rộng Prototype sẽ giúp lập trình viên tối ưu hóa bộ nhớ, cải thiện hiệu suất, và tổ chức mã nguồn một cách khoa học hơn.
Trong bài viết này, mình sẽ tìm hiểu về khái niệm Prototype, cách hoạt động của nó, cũng như so sánh với mô hình kế thừa dựa trên class trong JavaScript ES6.
Prototype là gì?
Trong JavaScript, Prototype là một cơ chế cho phép các object kế thừa thuộc tính và phương thức từ một object khác. Mỗi object trong JavaScript đều có một Prototype (ngoại trừ null
), và nó đóng vai trò như một khuôn mẫu chứa các phương thức hoặc thuộc tính mà object có thể truy cập.
Ví dụ, khi bạn tạo một object từ một constructor function hoặc một class, JavaScript tự động liên kết object đó với một Prototype. Nếu object không tìm thấy thuộc tính hoặc phương thức nào đó, JavaScript sẽ tìm kiếm trong Prototype của nó.
Mô hình Prototype-based trong JavaScript
JavaScript sử dụng mô hình Prototype-based inheritance thay vì class-based inheritance như các ngôn ngữ OOP truyền thống (Java, C++). Trong mô hình này, thay vì tạo lớp và định nghĩa phương thức trong đó, các object có thể kế thừa trực tiếp từ một object khác thông qua Prototype.
Ví dụ:
const person = { greet() { return "Hello!"; } }; const user = Object.create(person); // Tạo object user kế thừa từ person console.log(user.greet()); // "Hello!"
Ở đây, user
không có phương thức greet()
, nhưng vì nó kế thừa từ person
, JavaScript sẽ tìm greet()
trong Prototype của user
.
Mối quan hệ giữa Object và Prototype
-
Mọi object trong JavaScript đều có một Prototype, trừ
null
. -
Khi một object được tạo ra từ một Constructor Function, Prototype của Constructor Function sẽ được liên kết với object đó.
-
Các phương thức hoặc thuộc tính có thể được chia sẻ giữa nhiều object thông qua Prototype mà không cần sao chép dữ liệu.
Ví dụ về mối quan hệ giữa Object và Prototype:
function Person(name) { this.name = name; } Person.prototype.sayHello = function () { return `Hello, my name is ${this.name}`; }; const john = new Person("John"); console.log(john.sayHello()); // "Hello, my name is John"
Trong đoạn mã trên, phương thức sayHello()
không tồn tại trực tiếp trên object john
, mà nằm trong Prototype của Person
. Khi gọi john.sayHello()
, JavaScript sẽ tìm phương thức này trong Prototype của john
.
Prototype Chain (Chuỗi nguyên mẫu)
Prototype Chain là cơ chế mà JavaScript sử dụng để tìm kiếm thuộc tính hoặc phương thức trong object và các Prototype của nó. Nếu một object không chứa thuộc tính hoặc phương thức được gọi, JavaScript sẽ tiếp tục tìm trong Prototype của object đó, rồi đến Prototype của Prototype đó, cho đến khi gặp null
.
Ví dụ về Prototype Chain:
function Animal(name) { this.name = name; } Animal.prototype.makeSound = function () { return "Some sound"; }; const dog = new Animal("Dog"); console.log(dog.makeSound()); // "Some sound" console.log(dog.toString()); // "[object Object]" (được kế thừa từ Object.prototype) console.log(dog.__proto__ === Animal.prototype); // true console.log(Animal.prototype.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__); // null
Trong ví dụ trên:
-
dog
không có phương thứcmakeSound()
, nhưng nó tìm thấy trongAnimal.prototype
. -
Animal.prototype
không có phương thứctoString()
, nhưngObject.prototype
có, nên JavaScript lấy phương thức này từObject.prototype
. -
Nếu JavaScript không tìm thấy thuộc tính hoặc phương thức trong toàn bộ Prototype Chain, nó sẽ trả về
undefined
.
Truy cập và sử dụng Prototype trong JavaScript
__proto__
vs Object.getPrototypeOf()
Có hai cách để truy cập Prototype của một object:
__proto__
(deprecated - không nên dùng trong sản xuất)
-
Đây là một thuộc tính không chính thức nhưng vẫn được nhiều trình duyệt hỗ trợ.
-
Cho phép truy cập trực tiếp vào Prototype của một object.
const obj = {}; console.log(obj.__proto__ === Object.prototype); // true
Object.getPrototypeOf(obj)
(cách chính thống, an toàn hơn)
Đây là phương thức chính thức để lấy Prototype của một object.
const obj = {}; console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
Cách kiểm tra Prototype của một Object
Dùng Object.getPrototypeOf()
function Person() {} const john = new Person(); console.log(Object.getPrototypeOf(john) === Person.prototype); // true
Dùng instanceof
để kiểm tra kế thừa Prototype
console.log(john instanceof Person); // true console.log(john instanceof Object); // true (vì Person kế thừa từ Object)
Dùng isPrototypeOf()
để kiểm tra một Prototype có phải là Prototype của Object không
console.log(Person.prototype.isPrototypeOf(john)); // true console.log(Object.prototype.isPrototypeOf(john)); // true
Kế thừa bằng Prototype trong JavaScript
Cách Object kế thừa thuộc tính và phương thức thông qua Prototype
Trong JavaScript, khi một object được tạo ra từ một constructor function, nó sẽ tự động kế thừa các thuộc tính và phương thức từ Prototype của constructor đó. Nếu một thuộc tính hoặc phương thức không được tìm thấy trong object, JavaScript sẽ tìm kiếm nó trong Prototype Chain cho đến khi gặp null.
Ví dụ về kế thừa trong JavaScript bằng Prototype
Ví dụ 1: Kế thừa phương thức thông qua Prototype
function Animal(name) { this.name = name; } // Thêm phương thức vào prototype của Animal Animal.prototype.makeSound = function () { return `${this.name} is making a sound`; }; const dog = new Animal("Dog"); console.log(dog.makeSound()); // "Dog is making a sound" // Kiểm tra kế thừa console.log(dog.__proto__ === Animal.prototype); // true console.log(Animal.prototype.__proto__ === Object.prototype); // true