JavaScript là một ngôn ngữ OOP dựa trên nguyên mẫu chứ không chứa lớp. Đó là một trong những nguyên nhân của tất cả sự nhầm lẫn này bạn có thể thấy xung quanh: rất nhiều nhà phát triển sử dụng JS vì nó là một lớp dựa trên.
Vì vậy, bạn có thể thấy rất nhiều thư viện cố gắng sử dụng mô hình của ngôn ngữ dựa trên lớp trong JS.
Ngoài ra, bản thân JS thiếu một số đặc điểm làm cho di sản thừa kế, cũng dựa trên nguyên mẫu, đau đớn. Ví dụ, trước Object.create
không có cách nào để tạo một đối tượng từ một đối tượng khác (như một ngôn ngữ dựa trên nguyên mẫu OOP nên làm), nhưng chỉ sử dụng một hàm xây dựng function
.
Và cũng vì lý do đó, bạn có thể thấy rất nhiều thư viện đang cố gắng "sửa" ngôn ngữ, mọi ngôn ngữ theo cách riêng của nó.
Vì vậy, sự nhầm lẫn của bạn là bình thường. Nói như vậy, cách "chính thức" được sử dụng thuộc tính prototype
, function
s làm hàm tạo và Object.create
để tạo từ bản năng của đối tượng. Tuy nhiên, như đã nói ở trên, trong một số trường hợp mã là tiết hoặc thiếu chức năng, do đó, thủ tục này thường được bọc bằng cách nào đó, và sau đó nó sẽ trở thành một vấn đề sở thích cá nhân.
Vì bạn đang nói về mô hình, bạn có thể xem Backbone's Model và xem liệu phương pháp này có phù hợp với bạn không.
Cập nhật: để đưa ra một số ví dụ mà tôi hy vọng sẽ giúp bạn làm sáng tỏ, ở đây thừa kế học theo cách cũ sử dụng new
:
function Model() {
this.foo = "foo";
this.array = [];
}
Model.prototype.foo = "";
Model.prototype.array = null;
Model.prototype.doNothing = function() {};
function RestModel() {
this.bar = "bar";
}
RestModel.prototype = new Model;
RestModel.prototype.bar = ""
var myModel = new RestModel();
Bây giờ, ở đây các vấn đề:
- Các nhà xây dựng của
Model
được gọi một lần, chỉ khi số RestModel.prototype
được đặt. Do đó, thuộc tính foo
cho mọi trường hợp RestModel
sẽ là "foo".
- Không chỉ vậy, nhưng tất cả
RestModel
trường hợp sẽ chia sẻ cùng một phiên bản của cùng một mảng trong thuộc tính this.array
. Nếu bạn tạo một cá thể của Model
trực tiếp, bạn có một phiên bản mảng mới cho từng trường hợp.
myModel.constructor
là Model
thay vì RestModel
. Đó là vì chúng tôi ghi đè prototype
với một phiên bản mới là Model
, có chứa constructor
bằng với Model
.
- Nếu bạn muốn có một phiên bản mới của
RestModel
với một cuộc gọi lười biếng đến nhà xây dựng, là không thể.
Tôi nghĩ rằng có những điểm chính, tất nhiên chúng không phải là điểm duy nhất. Vì vậy, làm thế nào để giải quyết vấn đề đó? Như tôi đã nói, rất nhiều người đã làm điều đó, và họ tạo ra thư viện và các khuôn khổ cũng để hạn chế sự rách rưới. Tuy nhiên, để có một ý tưởng:
function Model() {
this.foo = "foo";
this.array = [];
}
Model.prototype.foo = "";
Model.prototype.array = null;
Model.prototype.doNothing = function() {};
function RestModel() {
Model.call(this);
this.bar = "bar";
}
RestModel.prototype = Object.create(Model.prototype);
RestModel.prototype.constructor = RestModel;
RestModel.prototype.bar = ""
var myModel = new RestModel();
// for lazy constructor call
var anotherModel = Object.create(RestModel.prototype);
RestModel.call(anotherModel);
Chú ý rằng nếu bạn không muốn sử dụng prototype
hoặc function
như constructor, với Object.create
bạn có thể làm điều đó:
var Model = {
foo: "",
array: null,
doNothing: function() {}
}
var RestModel = Object.create(Model);
RestModel.bar = "";
var myModel = Object.create(RestModel);
// Assuming you have to set some default values
initialize(RestModel);
Hoặc:
var Model = {
foo: "",
array: null,
doNothing: function() {},
init : function() {
this.foo = "foo";
this.array = [];
},
}
var RestModel = Object.create(Model);
RestModel.bar = "";
RestModel.init = function() {
Model.init.call(this);
this.bar = "bar";
}
var myModel = Object.create(RestModel);
myModel.init();
Trong trường hợp đó, bạn bắt chước cơ bản hàm tạo. Bạn cũng có thể vượt qua một số descriptor
đến Object.create
(xem docs) nhưng nó trở nên tiết kiệm hơn. Hầu hết mọi người sử dụng một loại hàm hoặc phương pháp extend
(như bạn có thể thấy trong ví dụ Backbone).
Hy vọng điều đó sẽ hữu ích!
Tại sao 'Object.Create' tốt hơn' mới' trong trường hợp đó? Nếu tôi muốn hỗ trợ các nhà thầu thì sao? Và hàm tạo được gọi trong cả hai 'RestModel' và' Model' (nếu tôi tạo một 'RestModel'). Tôi thường là một C# dev, vì vậy tôi không nhận được sự khác biệt thực sự. Tôi muốn xác định nguyên mẫu cơ sở và sử dụng một constructor, nhưng tôi không hiểu làm thế nào tôi nhận được cả hai? – jgauffin
Tôi nghĩ câu trả lời này sẽ tóm tắt bằng * "Sử dụng cái thích hợp." Đôi khi bạn muốn logic trong hàm tạo, đôi khi không. +1 –