1. Prototype là gì?
Nguồn: devRant
Trong JavaScript, Prototype là cơ chế cho phép các đối tượng (object) "thừa kế" các phương thức và thuộc tính từ các đối tượng khác. Mỗi object trong JavaScript đều có một liên kết ẩn gọi là [[Prototype]]
, thường được truy cập thông qua __proto__
hoặc thông qua Object.getPrototypeOf()
. Khi bạn tạo một object từ một hàm khởi tạo (constructor function), nó sẽ tự động liên kết với thuộc tính prototype
của hàm đó.
Ví dụ:
function Animal(name) { this.name = name;
} Animal.prototype.speak = function() { console.log(`${this.name} makes a sound.`);
}; const dog = new Animal('Dog');
dog.speak(); // Dog makes a sound.
Trong ví dụ này:
Animal
là một constructor function.Animal.prototype
chứa phương thứcspeak
, và đối tượngdog
được tạo ra từAnimal
có thể gọi được phương thức này vì nó có prototype liên kết đếnAnimal.prototype
.
2. Prototype Chain
Prototype Chain là khái niệm mô tả cách các đối tượng liên kết với nhau thông qua prototype. Khi bạn gọi một phương thức hoặc thuộc tính trên một object, JavaScript sẽ tìm kiếm nó trong object đó trước. Nếu không tìm thấy, nó sẽ tiếp tục tìm trong prototype của object, rồi đến prototype của prototype, cứ thế tiếp tục cho đến khi gặp null
(đỉnh của chuỗi prototype).
Ví dụ về Prototype Chain:
function A() {}
function B() {} // Thiết lập chuỗi prototype, cho phép B thừa kế các phương thức từ A
Object.setPrototypeOf(B.prototype, A.prototype); A.prototype.foo = function() { console.log('foo');
}
B.prototype.bar = function() { console.log('bar');
} // Thêm một phương thức vào Object.prototype, cho phép tất cả các object sử dụng phương thức này
Object.prototype.barbaz = function() { console.log('barbaz');
} // Thêm một phương thức vào Function.prototype, cho phép tất cả các hàm (function) sử dụng phương thức này
Function.prototype.foobar = function() { console.log('foobar');
} var a = new A(); // Tạo một đối tượng a từ constructor A
var b = new B(); // Tạo một đối tượng b từ constructor B // Thêm phương thức baz trực tiếp vào object b
b.baz = function() { console.log('baz');
} // Các lệnh dưới đây mô tả cách hoạt động của chuỗi prototype b.baz(); // 'baz', phương thức baz nằm trực tiếp trong object b, không thông qua prototype chain b.bar(); // 'bar', vì bar nằm trong B.prototype, được truy cập thông qua prototype chain b.foo(); // 'foo', vì foo nằm trong A.prototype (do B.prototype liên kết đến A.prototype) b.barbaz(); // 'barbaz', vì barbaz nằm trong Object.prototype, được liên kết mặc định bởi A.prototype // b.foobar(); // Gây ra lỗi TypeError: b.foobar is not a function vì foobar không nằm trong prototype chain của object b, vì thế engine sẽ trả về undefined vì không tìm thấy foobar
Lưu ý quan trọng về [[Prototype]]
và .prototype
:
-
Mọi object đều có
[[Prototype]]
: Đây là liên kết ẩn (internal link) mà mọi object trong JavaScript đều có.[[Prototype]]
xác định object đó liên kết đến đối tượng nào trong chuỗi prototype. Nó có thể được truy cập thông qua__proto__
hoặcObject.getPrototypeOf()
. -
Function cũng là object: Trong JavaScript, hàm (function) thực chất là một object. Do đó, nó cũng có liên kết
[[Prototype]]
. Bên cạnh đó, các function có một thuộc tính đặc biệt gọi là.prototype
(được hiển thị màu xanh dương trong sơ đồ). Thuộc tính này chứa các phương thức và thuộc tính chung mà các object khác có thể liên kết, chẳng hạn như các phương thức cơ bản củaObject.prototype
nhưtoString()
,valueOf()
, hayhasOwnProperty()
, hoặc các phương thức nhưbind()
,call()
,apply()
củaFunction.prototype
. Bản thân.prototype
cũng là một object nên cũng có[[Prototype]]
riêng, tạo ra một chuỗi liên kết trong prototype chain.
Nguồn: Dan D Kim
Sơ đồ Prototype Chain:
Trong ví dụ trên:
B.prototype
có liên kết vớiA.prototype
thông quaObject.setPrototypeOf()
.- Đối tượng
b
có thể truy cập các phương thức từ cảB.prototype
,A.prototype
vàObject.prototype
nhờ prototype chain. [[Prototype]]
của các thuộc tính.prototype
không liên quan gì đến các[[Prototype]]
của chính bản thân các function điều này dẫn đến việc dù[[Prototype]]
củafunction B
có tham chiếu đếnFunction.prototype
nhưngb.foobar()
đã không được tìm thấy và trả về lỗi khi thực thi
3. Khi nào cần chú ý đến prototype chain?
- Hiệu suất: Nếu prototype chain quá dài, việc tìm kiếm các thuộc tính hoặc phương thức trong chuỗi prototype có thể ảnh hưởng đến hiệu suất.
- Ghi đè phương thức: Bạn có thể ghi đè phương thức trong chuỗi prototype. Tuy nhiên, hãy cẩn thận vì điều này có thể gây ra xung đột hoặc làm thay đổi hành vi không mong muốn.
- Chia sẻ phương thức: Prototype là cách tuyệt vời để chia sẻ phương thức giữa các đối tượng mà không phải sao chép chúng vào mỗi đối tượng. Điều này giúp tiết kiệm bộ nhớ.
Kết luận
Hiểu về prototype và prototype chain là một trong những khái niệm cốt lõi của JavaScript. Nó cung cấp một cơ chế mạnh mẽ cho việc tái sử dụng và chia sẻ phương thức giữa các đối tượng. Bằng cách hiểu rõ cách hoạt động của prototype chain, bạn sẽ có thể viết mã JavaScript tối ưu và linh hoạt hơn.