0

我有一个主干视图,它的渲染函数中有以下代码(注意 3 console.log(that.ticketSelector.attributes);):

    if(typeof this.ticketSelector === 'undefined') {
        // TODO: first fetch tickets
        this.ticketSelector = new DataTable({
            collection: that.model.tickets,

            icon: "ticket",
            title: "Tickets",
            customClasses: ["ticket", "subject-selector"],

            columns: [
                {text: 'Name', modelKey: "name", col: 1},
                {text: 'Date', modelKey: "date", col: 2},
                {text: 'Owner', modelKey: "owner", col: 3},
                {text: 'Description', modelKey: "description", col: 4}
            ]
        });
        this.$('.subject-selectors').append(this.ticketSelector.$el);
        this.ticketSelector.render().resize();
    } else {
        this.ticketSelector.render().resize();
    }

    console.log(that.ticketSelector.attributes);

    if(typeof this.alarmSelector === 'undefined') {
        // TODO: first fetch tickets
        this.alarmSelector = new DataTable({
            collection: that.model.alarms,

            icon: "warning-sign",
            title: "Alarms",
            customClasses: ["alarm", "subject-selector"],

            columns: [
                {text: 'Name', modelKey: "name", col: 1},
                {text: 'Date', modelKey: "date", col: 2},
                {text: 'Owner', modelKey: "owner", col: 3},
                {text: 'Description', modelKey: "description", col: 4}
            ]
        });
        this.$('.subject-selectors').append(this.alarmSelector.$el);
        this.alarmSelector.render().resize();
    } else {
        this.alarmSelector.render().resize();
    }

    console.log(that.ticketSelector.attributes);

    if(typeof this.taskSelector === 'undefined') {
        // TODO: first fetch tickets
        this.taskSelector = new DataTable({
            collection: that.model.tasks,

            icon: "tasks",
            title: "Tasks",
            customClasses: ["task", "subject-selector"],

            columns: [
                {text: 'Name', modelKey: "name", col: 1},
                {text: 'Date', modelKey: "date", col: 2},
                {text: 'Owner', modelKey: "owner", col: 3},
                {text: 'Description', modelKey: "description", col: 4}
            ]
        });
        this.$('.subject-selectors').append(this.taskSelector.$el);
        this.taskSelector.render().resize();
    } else {
        this.taskSelector.render().resize();
    }

    console.log(that.ticketSelector.attributes);

在控制台中我看到:

Object {icon: "ticket", title: "Tickets", columns: Array[4], classes: Array[2]}
Object {icon: "warning-sign", title: "Alarms", columns: Array[4], classes: Array[2]}
Object {icon: "tasks", title: "Tasks", columns: Array[4], classes: Array[2]}

我期望的地方:

Object {icon: "ticket", title: "Tickets", columns: Array[4], classes: Array[2]}
Object {icon: "ticket", title: "Tickets", columns: Array[4], classes: Array[2]}
Object {icon: "ticket", title: "Tickets", columns: Array[4], classes: Array[2]}

这是我的数据表视图:

var DataTable = Backbone.View.extend({

tag: 'div',
className: 'data-table',

initialize: function(opts) {
    if(typeof opts.parent !== 'undefined') {
        this.parent = opts.parent;
    }
    if(typeof opts.icon !== 'undefined') {
        this.attributes.icon = opts.icon;
    }
    if(typeof opts.title !== 'undefined') {
        this.attributes.title = opts.title;
    }
    if(typeof opts.columns !== 'undefined') {
        this.attributes.columns = opts.columns;
    }
    if(typeof opts.customClasses !== 'undefined') {
        this.attributes.classes = opts.customClasses;
    }
},

attributes: {},

template: function() {
    var temp;

    $.ajax(root + 'templates/data-table.html', {
        success: function(data) {
            // console.log(data);
            temp = Mustache.compile($(data).filter('#data-table-template').html());
        },

        async: false
    });

    return temp;
}(),

events: {
},

serialize: function() {
    var that = this;
    return {
        root: root,
        icon: that.attributes.icon,
        title: that.attributes.title,
        columns: that.attributes.columns
    };
},

resize: function() {
    var that = this;
},

subView: [],

render: function() {
    var that = this;

    var html = this.template(this.serialize());

    this.$el.html(html);

    if(that.attributes.classes) {
        _.each(that.attributes.classes, function(c) {
            that.$el.addClass(c);
        });
    }

    this.collection.each(function(row) {
        tempView = new DataTableRow({ model: row, parent: that, columns: that.attributes.columns });
        that.subView.push(tempView);
        that.$('.tbody').append(tempView.$el);
        tempView.render();
    });

    this.$('.tbody').mCustomScrollbar({
        scrollInertia: 0,
    });

    return this;
}

});

var DataTableRow = Backbone.View.extend({

tag: 'div',
className: 'tr',

initialize: function(opts) {
    var that = this;

    if(typeof opts.parent !== 'undefined') {
        this.parent = opts.parent;
    }

    if(typeof opts.columns !== 'undefined') {
        var temp = {};
        that.attributes.columns = _.map(opts.columns, function(col) {
            return {
                text: that.model.get(col.modelKey),
                col: col.col
            };
        });
    }
},

attributes: { columns: [] },

template: function() {
    var temp;

    $.ajax(root + 'templates/data-table.html', {
        success: function(data) {
            // console.log(data);
            temp = Mustache.compile($(data).filter('#data-table-row-template').html());
        },

        async: false
    });

    return temp;
}(),

events: {
},

serialize: function() {
    var that = this;
    return {
        root: root,
        columns: that.attributes.columns
    };
},

resize: function() {
    var that = this;
},

render: function() {
    var that = this;

    var html = this.template(this.serialize());

    this.$el.html(html);

    return this;
}

});

我知道我可以通过为每个数据表创建不同的视图来解决它,但这应该可行,我不知道为什么它不可行。这里有什么帮助吗?谢谢。

编辑:为了更好地理解这一点,这里是下划线扩展功能:

_.extend = function(obj) {
  each(slice.call(arguments, 1), function(source) {
    if (source) {
      for (var prop in source) {
        obj[prop] = source[prop];
      }
    }
  });
  return obj;
};

是什么让这些属性附加到原型上?

4

1 回答 1

3

attributes {}在原型上,因此它将在对象的所有实例中共享。您需要attributes为每个实例创建一个对象。

而不是这样的:

var DataTable = Backbone.View.extend({
  attributes: {}
});

您需要在每次初始化时创建一个新对象:

var DataTable = Backbone.View.extend({
  attributes: {},
  initialize: function(options){
    this.attributes = {};
  }
});

正如@muistooshort 指出的那样,您的subViews阵列DataTableRow.prototype.attributes也有这个问题。传递给的对象中的任何键值对extend都放在对象的原型上,这意味着对象的新实例都将共享这些属性。这就是您的所有函数最终在每个实例上结束的方式,但这也意味着其他所有内容也一样,因此您需要确保为每个实例正确初始化事物。

编辑

Backbone.View.extend与其他代码无关,_.extend只是它碰巧使用它有一个助手,就像任何其他代码一样。

  • _.extend接受多个对象并将它们的属性复制到第一个对象上。
  • Backbone.View.extend接受一个对象,并返回一个新的构造函数,该构造函数将构造一个以该对象为原型的对象,并且该对象还继承了extend调用的第一个构造函数的原型。

https://github.com/jashkenas/backbone/blob/master/backbone.js#L1532

所以说你有一个像这样的对象:

Backbone.View构造函数在调用时将创建一个新对象,Backbone.View.prototype其原型具有标准视图方法。

当你这样做时var DataTable = Backbone.View.extend(newproto_obj),你现在有这个:

DataTable构造函数在调用时将创建一个新对象,该对象的原型具有来自 的值newproto_obj,并Backbone.View.prototype作为该原型的原型。

这被称为原型链,这就是 JavaScript 进行继承的方式。当您创建一个新对象时,它没有自己的属性,除非它们由 Backbone 在构造函数或initialize函数中设置。如果您尝试访问对象上的属性但它不存在,它将查看其原型是否具有该名称的属性。因为原型对象是跨实例共享的单个公共对象,所以修改它会改变每个实例。

于 2013-08-20T05:12:14.623 回答