Cả hai mẫu mã mà bạn đã trình bày trong câu hỏi của mình đều sử dụng kế thừa nguyên mẫu. Trên thực tế, bất kỳ mã hướng đối tượng nào bạn viết bằng JavaScript đều là mô hình kế thừa nguyên mẫu. JavaScript đơn giản là không có tính kế thừa cổ điển. Điều này sẽ làm rõ ràng mọi thứ một chút:
Inheritance
|
+-----------------------------+
| |
v v
Prototypal Classical
|
+------------------------------+
| |
v v
Prototypal Pattern Constructor Pattern
Như bạn có thể thấy kế thừa nguyên mẫu và kế thừa cổ điển là hai mô hình kế thừa khác nhau. Một số ngôn ngữ như Self, Lua và JavaScript hỗ trợ kế thừa nguyên mẫu. Tuy nhiên hầu hết các ngôn ngữ như C ++, Java và C # đều hỗ trợ kế thừa cổ điển.
Tổng quan nhanh về lập trình hướng đối tượng
Cả kế thừa nguyên mẫu và kế thừa cổ điển đều là mô hình lập trình hướng đối tượng (tức là chúng xử lý các đối tượng). Đối tượng chỉ đơn giản là những trừu tượng đóng gói các thuộc tính của một thực thể trong thế giới thực (tức là chúng đại diện cho những thứ từ thực trong chương trình). Điều này được gọi là trừu tượng.
Tính trừu tượng: Sự thể hiện của những thứ trong thế giới thực trong các chương trình máy tính.
Về mặt lý thuyết, một sự trừu tượng được định nghĩa là "một khái niệm chung được hình thành bằng cách trích xuất các đặc điểm chung từ các ví dụ cụ thể". Tuy nhiên, vì lợi ích của lời giải thích này, chúng tôi sẽ sử dụng định nghĩa nói trên để thay thế.
Bây giờ một số đối tượng có rất nhiều điểm chung. Ví dụ như một chiếc xe đạp bùn và một chiếc Harley Davidson có rất nhiều điểm chung.
Một chiếc xe đạp bùn:
Một chiếc Harley Davidson:
Một chiếc xe đạp bùn và một chiếc Harley Davidson đều là xe đạp. Do đó, xe đạp là sự tổng hòa của cả xe đạp bùn và xe Harley Davidson.
Bike
|
+---------------------------------+
| |
v v
Mud Bike Harley Davidson
Trong ví dụ trên, xe đạp, xe đạp bùn và Harley Davidson đều là những bản trừu tượng. Tuy nhiên, xe đạp là một sự trừu tượng chung hơn của xe đạp bùn và Harley Davidson (tức là cả xe đạp bùn và Harley Davidson đều là những loại xe đạp cụ thể).
Khái quát hóa: Một sự trừu tượng của một sự trừu tượng cụ thể hơn.
Trong lập trình hướng đối tượng, chúng tôi tạo ra các đối tượng (là những trừu tượng của các thực thể trong thế giới thực) và chúng tôi sử dụng các lớp hoặc nguyên mẫu để tạo ra các khái quát của các đối tượng này. Khái quát hóa được tạo ra thông qua kế thừa. Một chiếc xe đạp là một khái quát của một chiếc xe đạp bùn. Do đó xe đạp bùn kế thừa từ xe đạp thồ.
Lập trình hướng đối tượng cổ điển
Trong lập trình hướng đối tượng cổ điển, chúng ta có hai loại trừu tượng: lớp và đối tượng. Một đối tượng, như đã đề cập trước đây, là một sự trừu tượng của một thực thể thế giới thực. Mặt khác, một lớp là sự trừu tượng của một đối tượng hoặc một lớp khác (tức là nó là một sự tổng quát hóa). Ví dụ, hãy xem xét:
+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | Man | Class of object johnDoe. |
| 3 | Human | Superclass of class Man. |
+----------------------+----------------+---------------------------------------+
Như bạn có thể thấy trong ngôn ngữ lập trình hướng đối tượng cổ điển, các đối tượng chỉ là trừu tượng (tức là tất cả các đối tượng có mức trừu tượng là 1) và các lớp chỉ là khái quát hóa (tức là tất cả các lớp có mức trừu tượng lớn hơn 1).
Các đối tượng trong ngôn ngữ lập trình hướng đối tượng cổ điển chỉ có thể được tạo bằng các lớp khởi tạo:
class Human {
// ...
}
class Man extends Human {
// ...
}
Man johnDoe = new Man();
Tóm lại trong ngôn ngữ lập trình hướng đối tượng cổ điển, các đối tượng là sự trừu tượng hóa của các thực thể trong thế giới thực và các lớp là sự tổng quát hóa (tức là sự trừu tượng hóa của một trong hai đối tượng hoặc các lớp khác).
Do đó, khi mức độ trừu tượng tăng lên, các thực thể trở nên tổng quát hơn và khi mức độ trừu tượng giảm thì các thực thể trở nên cụ thể hơn. Theo nghĩa này, mức độ trừu tượng tương tự như một thang đo từ các thực thể cụ thể hơn đến các thực thể tổng quát hơn.
Lập trình hướng đối tượng nguyên mẫu
Ngôn ngữ lập trình hướng đối tượng nguyên mẫu đơn giản hơn nhiều so với ngôn ngữ lập trình hướng đối tượng cổ điển vì trong lập trình hướng đối tượng nguyên mẫu chúng ta chỉ có một kiểu trừu tượng (tức là các đối tượng). Ví dụ, hãy xem xét:
+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | man | Prototype of object johnDoe. |
| 3 | human | Prototype of object man. |
+----------------------+----------------+---------------------------------------+
Như bạn có thể thấy trong các ngôn ngữ lập trình hướng đối tượng nguyên mẫu, các đối tượng là sự trừu tượng hóa của các thực thể trong thế giới thực (trong trường hợp đó chúng được gọi đơn giản là các đối tượng) hoặc các đối tượng khác (trong trường hợp đó chúng được gọi là nguyên mẫu của các đối tượng mà chúng trừu tượng hóa). Do đó, một nguyên mẫu là một sự tổng quát hóa.
Các đối tượng trong ngôn ngữ lập trình hướng đối tượng nguyên mẫu có thể được tạo ra từ ex-nihilo (tức là không có gì) hoặc từ một đối tượng khác (trở thành nguyên mẫu của đối tượng mới được tạo):
var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);
Theo quan điểm khiêm tốn của tôi, ngôn ngữ lập trình hướng đối tượng nguyên mẫu mạnh hơn ngôn ngữ lập trình hướng đối tượng cổ điển vì:
- Chỉ có một kiểu trừu tượng.
- Khái quát hóa đơn giản là đối tượng.
Đến đây chắc hẳn bạn đã nhận ra sự khác biệt giữa kế thừa cổ điển và kế thừa nguyên mẫu. Kế thừa cổ điển được giới hạn cho các lớp kế thừa từ các lớp khác. Tuy nhiên, kế thừa nguyên mẫu không chỉ bao gồm các nguyên mẫu kế thừa từ các nguyên mẫu khác mà còn bao gồm các đối tượng kế thừa từ nguyên mẫu.
Tính phân lập lớp nguyên mẫu
Bạn phải nhận thấy rằng các nguyên mẫu và các lớp rất giống nhau. Đúng. Họ đang. Trên thực tế, chúng giống nhau đến mức bạn thực sự có thể sử dụng các nguyên mẫu để mô hình hóa các lớp:
function CLASS(base, body) {
if (arguments.length < 2) body = base, base = Object.prototype;
var prototype = Object.create(base, {new: {value: create}});
return body.call(prototype, base), prototype;
function create() {
var self = Object.create(prototype);
return prototype.hasOwnProperty("constructor") &&
prototype.constructor.apply(self, arguments), self;
}
}
Sử dụng CLASS
hàm trên, bạn có thể tạo các nguyên mẫu trông giống như các lớp:
var Human = CLASS(function () {
var milliseconds = 1
, seconds = 1000 * milliseconds
, minutes = 60 * seconds
, hours = 60 * minutes
, days = 24 * hours
, years = 365.2425 * days;
this.constructor = function (name, sex, dob) {
this.name = name;
this.sex = sex;
this.dob = dob;
};
this.age = function () {
return Math.floor((new Date - this.dob) / years);
};
});
var Man = CLASS(Human, function (Human) {
this.constructor = function (name, dob) {
Human.constructor.call(this, name, "male", dob);
if (this.age() < 18) throw new Error(name + " is a boy, not a man!");
};
});
var johnDoe = Man.new("John Doe", new Date(1970, 0, 1));
Tuy nhiên, điều ngược lại là không đúng (tức là bạn không thể sử dụng các lớp để lập mô hình nguyên mẫu). Điều này là do nguyên mẫu là đối tượng nhưng các lớp không phải là đối tượng. Chúng là một kiểu trừu tượng hoàn toàn khác.
Phần kết luận
Tóm lại, chúng ta đã học được rằng trừu tượng là một "khái niệm chung được hình thành bằng cách trích xuất các đặc điểm chung từ các ví dụ cụ thể" và khái quát đó là "một trừu tượng của một trừu tượng cụ thể hơn" . Chúng tôi cũng đã tìm hiểu về sự khác biệt giữa kế thừa nguyên mẫu và cổ điển và cách cả hai đều là hai mặt của cùng một đồng tiền.
Trên một lưu ý chia tay, tôi muốn lưu ý rằng có hai mẫu kế thừa nguyên mẫu: mẫu nguyên mẫu và mẫu phương thức khởi tạo. Mẫu nguyên mẫu là mẫu chính tắc của kế thừa nguyên mẫu trong khi mẫu phương thức khởi tạo được sử dụng để làm cho kế thừa nguyên mẫu trông giống kế thừa cổ điển hơn. Cá nhân tôi thích mô hình nguyên mẫu hơn.
Tái bút Tôi là người đã viết bài đăng trên blog " Tại sao các vấn đề thừa kế nguyên mẫu " và trả lời câu hỏi " Lợi ích của việc kế thừa nguyên mẫu so với cổ điển? ". Câu trả lời của tôi là câu trả lời được chấp nhận.