2012-06-21 43 views
6

Tôi cố gắng để thực hiện phiên bản của tôi về "Instance Store" trong Backbone.js như mô tả của Soundcloud trong bài đăng blog gần đây của họ:Thực hiện javascript cửa hàng dụ bằng cách trả lại ví dụ hiện tại từ constructor

http://backstage.soundcloud.com/2012/06/building-the-next-soundcloud/

liên quan Trích:

Để giải quyết điều này, chúng tôi sử dụng cấu trúc mà chúng tôi gọi là cửa hàng cá thể. Cửa hàng này là một đối tượng được truy cập và sửa đổi hoàn toàn mỗi lần một hàm tạo cho một mô hình được gọi. Khi một mô hình được xây dựng lần đầu tiên, nó sẽ tự tiêm vào cửa hàng, sử dụng id của nó làm khóa duy nhất. Nếu cùng một hàm tạo mô hình được gọi với cùng một id, thì cá thể ban đầu được trả về.

var s1 = new Sound({id: 123}), 
    s2 = new Sound({id: 123}); 

s1 === s2; // true, these are the exact same object. 

Điều này hoạt động vì tính năng ít được biết đến đáng ngạc nhiên của Javascript. Nếu một hàm tạo trả về một đối tượng, thì đó là giá trị được gán. Do đó, nếu chúng ta trả về một tham chiếu đến cá thể được tạo trước đó, chúng ta sẽ có được hành vi mong muốn. Đằng sau hậu trường, người xây dựng cơ bản thực hiện việc này:

var store = {}; 

function Sound(attributes) { 
    var id = attributes.id; 

    // check if this model has already been created 
    if (store[id]) { 
     // if yes, return that 
     return store[id]; 
    } 
    // otherwise, store this instance 
    store[id] = this; 
} 

Tôi đã triển khai phiên bản này bằng cách ghi đè lớp Backbone.Model để tạo hàm tạo của riêng tôi.

var MyModel = Backbone.Model.extend({ 
    constructor: function (attributes, options) { 
     var id = attributes ? attributes.id : undefined; 

     if (this.store[id]) { 
      return this.store[id]; 
     } 

     Backbone.Model.prototype.constructor.apply(this, arguments); 

     if (id) { 
      this.store[id] = this; 
     } 
    } 
}); 

var MyOtherModel = MyModel.extend({ 
    store: {}, 

    //other model stuff 
}); 

Điều này hoạt động tốt, nhưng điều gì đó phải thay đổi và hiện đã ngừng hoạt động và tôi không chắc chắn lý do. Các cá thể mới tạo được lưu trữ trong đối tượng lưu trữ mà không có vấn đề - mỗi lớp mở rộng lớp MyModel có kho trống riêng của nó để tránh va chạm các cá thể của một kiểu khác với cùng một id. Ví dụ chính xác cũng được lấy ra mà không có vấn đề gì khi hàm tạo được gọi với một id hiện có, tuy nhiên khi chúng được trả về từ hàm khởi tạo, giá trị trả về bị bỏ qua. Sự hiểu biết của tôi từ spec là các nhà xây dựng có thể trả về một đối tượng - nhưng không phải là một nguyên thủy - và đối tượng trả về sẽ được gán cho bên trái của câu lệnh gán khi hàm tạo được gọi với toán tử mới. Điều này không xảy ra, mặc dù hàm khởi tạo trả về một đối tượng, đối tượng rỗng được tạo bởi toán tử mới được sử dụng.

Một số thông tin gỡ lỗi. Không chắc chắn thông tin này hữu ích như thế nào. Đây là "this" trong constructor MyModel cho một đối tượng được khởi tạo lần đầu tiên.

child 
    _callbacks: Object 
    _escapedAttributes: Object 
    _previousAttributes: Object 
    _setting: false 
    attributes: Object 
     id: "4fd6140032a6e522f10009ac" 
     manufacturer_id: "4f4135ae32a6e52a53000001" 
     name: "Tide" 
     uniqueName: "tide" 
    __proto__: Object 
    cid: "c50" 
    collection: child 
    id: "4fd6140032a6e522f10009ac" 
    __proto__: ctor 
     constructor: function(){ parent.apply(this, arguments); } 
     defaults: Object 
     store: Object 
     url: function() { 
     urlRoot: function() { 
     __proto__: ctor 

Và đây là "này" trong constructor MyModel khi nó một đối tượng được trả lại từ các cửa hàng Ví dụ:

child 
    _callbacks: Object 
    _escapedAttributes: Object 
    _previousAttributes: Object 
    _setting: false 
    attributes: Object 
     _validate: function (attrs, options) { 
     bind: function (events, callback, context) { 
     change: function (options) { 
     changedAttributes: function (diff) { 
     clear: function (options) { 
     clone: function() { 
     constructor: function(){ parent.apply(this, arguments); } 
     defaults: Object 
     destroy: function (options) { 
     escape: function (attr) { 
     fetch: function (options) { 
     get: function (attr) { 
     has: function (attr) { 
     hasChanged: function (attr) { 
     idAttribute: "id" 
     initialize: function(){} 
     isNew: function() { 
     isValid: function() { 
     manufacturer_id: 0 
     name: "" 
     off: function (events, callback, context) { 
     on: function (events, callback, context) { 
     parse: function (resp, xhr) { 
     previous: function (attr) { 
     previousAttributes: function() { 
     save: function (key, value, options) { 
     set: function (key, value, options) { 
     store: Object 
     toJSON: function() { 
     trigger: function (events) { 
     unbind: function (events, callback, context) { 
     unset: function (attr, options) { 
     url: function() { 
     urlRoot: function() { 
     __proto__: Object 
     cid: "c141" 
    __proto__: ctor 
     constructor: function(){ parent.apply(this, arguments); } 
     defaults: Object 
     store: Object 
     url: function() { 
     urlRoot: function() { 
     __proto__: ctor 

Những gì tôi lưu ý là các thuộc tính đối tượng trong một giây có tất cả các phương pháp của một đối tượng xương sống bao gồm trong đó, mà họ không nên. Nó cũng không có id, một lần nữa tôi không chắc tại sao. Hy vọng rằng điều này cung cấp một số cái nhìn sâu sắc. Cảm ơn.

+0

Ông có thể cho chúng ta một deb ug view của MyModel? – Bergi

+0

Chắc chắn. Xin lỗi vì sự thiếu hiểu biết của tôi nhưng bạn có thể chỉ định chính xác những gì bạn cần không? JavaScript gỡ lỗi người mới ở đây, gỡ lỗi của tôi thường bao gồm các câu lệnh console.log. – Kareem

+0

Vâng, điều đó thật tuyệt. Tôi đoán nó là một chức năng, sau đó mã của nó và có thể là phạm vi biến nó có quyền truy cập vào sẽ là thú vị. – Bergi

Trả lời

3

Tôi sẽ không sử dụng mở rộng cho điều này, tôi nghĩ rằng có một "nhà máy" riêng biệt là ý tưởng đúng. Nó sẽ cho phép bạn mở rộng mô hình của bạn mà không sợ những tác dụng phụ.

Từ xương sống annotated source có một số công cụ kỳ lạ với mở rộng, tôi đã không hoàn toàn quấn quanh đầu của tôi. (Ngoài ra hãy kiểm tra inherits) Vì vậy, hãy bỏ qua nó ngay bây giờ và gắn bó với giải pháp làm việc của bạn.

Tôi đã sửa đổi phương pháp của bạn để tạo mô hình nhà máy, bạn có thể sử dụng chúng như mô hình bình thường (ví dụ, đặt chúng trên bộ sưu tập) ngoại trừ việc mở rộng chúng sẽ không hoạt động.Họ cũng sẽ xử lý việc cập nhật mô hình của bạn bằng dữ liệu mới như ví dụ soundcloud.

var makeStoreable = function(model){ 
    var StoreModel = function(attr, opt){ 
    if(!attr || !attr.id){ 
     // The behavior you exhibit here is up to you 
     throw new Error('Cool Models always have IDs!'); 
    } 
    if(this.store[attr.id]){ 
     this.store[attr.id].set(attr, opt); 
    }else{ 
     var newModel = new model(attr, opt); 
     this.store[attr.id] = newModel; 
    } 
    return this.store[attr.id]; 
    }; 
    StoreModel.prototype.store = {}; 
    return StoreModel; 
}; 

var CoolModel = Backbone.Model.extend({}); 

CoolModel = makeStoreable(CoolModel); 

var a = new CoolModel({ 
    id: 4, 
    coolFactor: 'LOW' 
}); 

var b = new CoolModel({ 
    id:4, 
    coolFactor: 'HIGH' 
}); 

console.log(a===b); //true! 
console.log(a.get('coolFactor') === 'HIGH'); //true! 

here's a fiddle để phát xung quanh.

Ngoài ra, tôi muốn chào đón một người nào đó đưa ra giải pháp mô hình trong việc giữ "cửa hàng" trong mẫu thử nghiệm của cá thể mẫu. Ngoài ra để ngăn chặn rò rỉ bộ nhớ, có lẽ chúng ta nên tạo một phương thức tính toán tham chiếu đếm, hoặc trên nhà máy hoặc chính mô hình đó.

+0

Tôi đang làm việc với giải pháp mà tôi đã yêu cầu https://github.com/reconbot/backbone-singleton – reconbot

+0

Ok tôi đã làm xong, sử dụng giải pháp của tôi. – reconbot

+0

Và cập nhật nó để hoạt động với xương sống mới nhất – reconbot

2

Cách tiếp cận của wizard có vẻ khá đẹp và sạch sẽ. +1 cho điều đó.

Cách nó được triển khai trong SoundCloud là bằng cách ghi đè phương thức Backbone.Model.extend để tạo lớp với hàm tạo đã sửa đổi của chúng tôi và cửa hàng đóng. Cửa hàng ban đầu được tạo ra trong một đóng cửa để giữ cho giao diện của lớp sạch, nhưng sau một thời gian nó được tìm thấy là hữu ích để gỡ lỗi để có một tham chiếu đến cửa hàng của mỗi lớp, vì vậy nó cũng được đính kèm ở đó.

Chúng tôi có số tham chiếu để sử dụng bộ nhớ không phát nổ và cũng cung cấp cho lớp khả năng xác định hàm tùy chỉnh cung cấp giá trị duy nhất để xác định nó. Hầu hết thời gian id là đủ, nhưng có một số trường hợp góc mà nó không hoàn toàn làm việc ra ngoài.

tôi muốn chào đón một người nào đó để đưa ra một giải pháp trong mô hình giữ gìn "cửa hàng" trong nguyên mẫu các trường hợp mô hình của

Bạn có thể làm myInstance.constructor.store

+0

Bạn đã bao giờ thấy Lucky Number Slevin chưa? –

+0

Bạn có muốn chia sẻ một chút mã không? :) –

0

Sau giải pháp sử dụng @reconbot tôi đã tìm thấy nó brokes toán tử instanceof:

(new CoolModel) instanceof CoolModel // FALSE!!! 

var MyModel = Backbone.Model.extend({ 
    idAttribute: 'myId' 
}); 
new MyModel({ myId: 1 }) === new MyModel({ myId: 1 }) // FALSE! 

tôi đã phát triển một phiên bản mới, những người sử dụng tài sản id riêng của mô hình (thông qua idAttribute) và làm việc với instanceof và cho phép bạn mở rộng nhà máy:

FIDDLE

function makeStoreable(model) { 
    var store = {}; 
    var idField = model.prototype.idAttribute; 

    function ModelFactory(attr, opt) { 
     if (!attr || !(idField in attr)) { 
      throw new Error('Cool Models always have IDs!'); 
     } 

     var id = attr[idField]; 

     if (store.hasOwnProperty(id)) { 
      store[id].set(attr, opt); 
     } else { 
      model.call(this, attr, opt); 
      store[id] = this; 
     } 

     return store[id]; 
    } 

    function intermediate() {} 
    intermediate.prototype = model.prototype; 
    ModelFactory.prototype = new intermediate; 

    // Only EcmaScript5! 
    // ModelFactory.extend = model.extend.bind(model); 
    ModelFactory.extend = function() { 
     return model.extend.apply(model, arguments); 
    }; 

    return ModelFactory; 
} 

Và kiểm tra:

var RareID = Backbone.Model.extend({ 
    idAttribute: '_myOwnServerId' 
}); 
RareID = makeStoreable(RareID); 

var a = new RareID({ 
    _myOwnServerId: 4, 
    coolFactor: 'LOW' 
}); 

var b = new RareID({ 
    _myOwnServerId: 4, 
    coolFactor: 'HIGH' 
}); 

console.log(a===b); //true! 
console.log(a instanceof RareID); //true! 
console.log(a.get('coolFactor') === 'HIGH'); //true! 

:)

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