Реализовать хранилище экземпляров javascript, возвращая существующий экземпляр из конструктора

Я пытаюсь реализовать свою версию «Instance Store» в Backbone.js, как описано Soundcloud в их недавнем сообщении в блоге :

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

. Соответствующая выдержка:

Чтобы решить эту проблему, мы используем конструкцию, которую называем хранилищем экземпляров. Это хранилище является объектом, к которому неявно обращаются и изменяют каждый раз, когда вызывается конструктор для модели. Когда модель создается впервые, она внедряется в хранилище, используя свой идентификатор в качестве уникального ключа. Если тот же конструктор модели вызывается с тем же идентификатором, то возвращается исходный экземпляр.

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

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

Это работает из-за удивительно малоизвестной -особенности Javascript. Если конструктор возвращает объект, то это присваиваемое значение. Поэтому, если мы вернем ссылку на созданный ранее экземпляр, мы получим желаемое поведение. За кулисами этим в основном занимается конструктор:

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;
}

Я реализовал свою версию этого, переопределив класс Backbone.Model для создания собственного конструктора.

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
});

Это работало просто отлично, но что-то должно было измениться, и теперь оно перестало работать, и я не знаю, почему. Вновь созданные экземпляры сохраняются в объекте хранилища без проблем. -Каждый класс, расширяющий класс MyModel, имеет собственное пустое хранилище, чтобы избежать коллизий экземпляров другого типа с одним и тем же идентификатором. Правильный экземпляр также извлекается без проблем, когда конструктор вызывается с существующим идентификатором, однако, когда они возвращаются из конструктора, возвращаемое значение игнорируется. Насколько я понимаю из спецификации, конструкторы могут возвращать объект -, но не примитив -, и возвращаемый объект будет присваиваться левой части оператора присваивания, когда конструктор вызывается с оператором new.Этого не происходит, несмотря на то, что конструктор возвращает объект, используется пустой объект, созданный оператором new.

Немного отладочной информации. Не уверен, что эта информация будет полезна. Это «это» в конструкторе MyModel для объекта, который создается в первый раз.

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

И это «это» в конструкторе MyModel, когда это объект, возвращаемый из хранилища экземпляров:

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

Что я отмечаю, так это то, что объект атрибутов во втором имеет все методы магистрального объекта, включенные туда, чего не должно быть. У него также нет идентификатора, опять же, я не уверен, почему. Надеюсь, это дает некоторое представление. Спасибо.

6
задан Kareem 21 June 2012 в 21:28
поделиться