2012-06-05 37 views
13

Tôi đã đọc rất nhiều bài viết về "kế thừa" trong javascript. Một số người trong số họ sử dụng new trong khi một số khác đề xuất Object.Create. Càng đọc, tôi càng bối rối vì dường như tồn tại vô số biến thể để giải quyết kế thừa.Kế thừa javascript đúng

Có thể ai đó tốt bụng để chỉ cho tôi cách được chấp nhận nhất (hoặc tiêu chuẩn defacto nếu có)?

(Tôi muốn có một đối tượng cơ sở Model mà tôi có thể mở rộng RestModel hoặc LocalStorageModel.)

Trả lời

11

đơn giản: Object.create không được hỗ trợ trong tất cả các môi trường, nhưng có thể được shimmed với new. Ngoài ra, hai mục tiêu khác nhau: Object.create chỉ tạo đối tượng kế thừa từ một số khác, trong khi newcũng gọi hàm dựng. Sử dụng những gì là thích hợp.

Trong trường hợp của bạn, bạn dường như muốn rằng số tiền RestModel.prototype được kế thừa từ Model.prototype. Object.create (hoặc shim của nó) là cách chính xác sau đó, bởi vì bạn không muốn a) tạo một đối tượng mới (thuyết minh một new Model) và b) không muốn gọi các nhà xây dựng mẫu:

RestModel.prototype = Object.create(Model.prototype); 

Nếu bạn muốn gọi hàm dựng Model trên RestModels, nó không liên quan gì tới các nguyên mẫu. Sử dụng call() hoặc apply() cho rằng:

function RestModel() { 
    Model.call(this); // apply Model's constructor on the new object 
    ... 
} 
+0

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

+1

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 –

10

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 đề:

  1. 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".
  2. 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.
  3. myModel.constructorModel 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.
  4. 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!

+0

Bạn khuyến nghị sử dụng ví dụ nào? – jgauffin

+0

Tất cả chúng đều hợp lệ, nhưng ở dạng này hay dạng khác, chúng sẽ kết thúc. Bạn có thể chụp ảnh nếu 'RestModel' có nhiều thuộc tính hoặc phương thức mới chẳng hạn. Đó là những gì tôi đã nói khi tôi nói rằng JS vẫn thiếu một cái gì đó. Để loại bỏ độ dài, hoặc bạn quyết định sử dụng thư viện của bên thứ ba hoặc bạn tạo một cặp chức năng tiện ích để thực hiện điều đó. Tôi sẽ tránh phương pháp 'mới' thuần túy (đầu tiên), và chỉ quyết định cho thứ 2 hoặc thứ 3, nó phụ thuộc nếu bạn cảm thấy thoải mái khi có thứ gì đó" giống "một" class "(thứ 2) hay không (thứ 3)). – ZER0

Các vấn đề liên quan