Nguyên mẫu là một tối ưu hóa .
Một ví dụ tuyệt vời về việc sử dụng chúng tốt là thư viện jQuery. Mỗi khi bạn lấy một đối tượng jQuery bằng cách sử dụng $('.someClass')
, đối tượng đó có hàng tá "phương thức". Thư viện có thể đạt được điều đó bằng cách trả về một đối tượng:
return {
show: function() { ... },
hide: function() { ... },
css: function() { ... },
animate: function() { ... },
// etc...
};
Nhưng điều đó có nghĩa là mọi đối tượng jQuery trong bộ nhớ sẽ có hàng chục vị trí được đặt tên chứa các phương thức giống nhau, lặp đi lặp lại.
Thay vào đó, các phương thức đó được xác định trên một nguyên mẫu và tất cả các đối tượng jQuery "kế thừa" nguyên mẫu đó để đạt được tất cả các phương thức đó với chi phí thời gian chạy rất ít.
Một phần cực kỳ quan trọng trong cách jQuery làm đúng là điều này được ẩn với lập trình viên. Nó hoàn toàn được coi là một tối ưu hóa, không phải là thứ mà bạn phải lo lắng khi sử dụng thư viện.
Vấn đề với JavaScript là các hàm khởi tạo thường yêu cầu người gọi phải nhớ đặt tiền tố cho chúng new
hoặc nếu không thì chúng thường không hoạt động. Không có lý do chính đáng cho điều này. jQuery làm đúng bằng cách giấu điều vô nghĩa đó đằng sau một hàm thông thường $
, vì vậy bạn không cần quan tâm đến cách các đối tượng được triển khai.
Để bạn có thể thuận tiện tạo một đối tượng với một nguyên mẫu được chỉ định, ECMAScript 5 bao gồm một chức năng tiêu chuẩn Object.create
. Một phiên bản được đơn giản hóa rất nhiều của nó sẽ trông như thế này:
Object.create = function(prototype) {
var Type = function () {};
Type.prototype = prototype;
return new Type();
};
Nó chỉ lo việc viết một hàm khởi tạo và sau đó gọi nó bằng new
.
Khi nào bạn tránh các nguyên mẫu?
Một so sánh hữu ích là với các ngôn ngữ OO phổ biến như Java và C #. Chúng hỗ trợ hai loại kế thừa:
- kế thừa giao diện , trong đó bạn
implement
một interface
lớp cung cấp triển khai duy nhất của riêng nó cho mọi thành viên của giao diện.
- thực hiện kế thừa, nơi bạn có
extend
một class
cung cấp triển khai mặc định của một số phương pháp.
Trong JavaScript, kế thừa nguyên mẫu là một kiểu kế thừa triển khai . Vì vậy, trong những tình huống mà (trong C # hoặc Java), bạn sẽ bắt nguồn từ một lớp cơ sở để đạt được hành vi mặc định, sau đó bạn thực hiện các sửa đổi nhỏ thông qua ghi đè, thì trong JavaScript, kế thừa nguyên mẫu có ý nghĩa.
Tuy nhiên, nếu bạn đang ở trong tình huống mà bạn đã sử dụng giao diện trong C # hoặc Java, thì bạn không cần bất kỳ tính năng ngôn ngữ cụ thể nào trong JavaScript. Không cần phải khai báo rõ ràng một cái gì đó đại diện cho giao diện và không cần đánh dấu các đối tượng là "triển khai" giao diện đó:
var duck = {
quack: function() { ... }
};
duck.quack(); // we're satisfied it's a duck!
Nói cách khác, nếu mỗi "loại" đối tượng có định nghĩa riêng về "phương thức", thì sẽ không có giá trị nào trong việc kế thừa từ một nguyên mẫu. Sau đó, nó phụ thuộc vào số lượng phiên bản bạn phân bổ của mỗi loại. Nhưng trong nhiều thiết kế mô-đun, chỉ có một trường hợp của một kiểu nhất định.
Và trên thực tế, nhiều người đã cho rằng kế thừa thực hiện là xấu xa . Nghĩa là, nếu có một số hoạt động phổ biến cho một kiểu, thì có lẽ sẽ rõ ràng hơn nếu chúng không được đưa vào một lớp cơ sở / siêu lớp, mà thay vào đó chỉ được hiển thị như các hàm thông thường trong một số mô-đun, mà bạn truyền (các) đối tượng vào. bạn muốn chúng hoạt động.