2

我在 Backbone 中看到了这个关于继承的问题: Backbone.js 视图继承。有用但不回答我的问题。

我遇到的问题是这样的:

假设我有一个类Panel(本例中的模型);

var Panel = Backbone.Model.extend({
    defaults : {
        name : 'my-panel'
    }
});

然后一个AdvancedPanel;

var AdvancedPanel = Panel.extend({
    defaults : {
        label : 'Click to edit'
    }
});

以下不起作用:

var advancedPanel = new AdvancedPanel();
alert(advancedPanel.get('name')); // Undefined :(

JSFiddle在这里:http: //jsfiddle.net/hWmnb/

我想我可以看到我可以通过一些自定义扩展函数自己实现这一点,该函数创建原型的深层副本,但这似乎是人们可能希望从 Backbone 继承中获得的常见事情,有没有标准的方法来做到这一点?

4

4 回答 4

3

建立原型链的 Backboneextend函数是通用的,用于视图、路由器、模型和集合。因此它不知道诸如defaults or之类的细节events,也无法确定扩展类型是否要覆盖合并父级中的值。

一种前进的方法是在实际定义之外明确地做到这一点:

AdvancedPanel.prototype.defaults = _.extend({}, Panel.prototype.defaults, {
  label: 'Click me'
});

你也会对视图做同样的事情:

SomeExtendedView.prototype.events = _.extend({}, SomeBaseView.prototype.events, {
  'click button': 'onClick'
});

如果您愿意,您实际上也可以在适当的位置列出默认值。您只需在构造函数定义之外进行合并:

var Base = Backbone.Model.extend({
  defaults: {
    base: 123
  }
});

var Extended = Base.extend({
  defaults: {
    extended: 456
  }
});

Extended.prototype.defaults = _.extend({}, Base.prototype.defaults, Extended.prototype.defaults);

这将Extended.defaults包含两者baseextended以及传递给实例化的任何属性:

console.log(JSON.stringify(new Extended({ test: 'Hey' }).toJSON()));
>> {"base":123,"extended":456,"test":"Hey"}
于 2012-10-01T13:58:30.713 回答
2

拿起这个,我写了一个简单的advancedExtend方法来替换行(来自这个继承结构):

Panel.extend = Backbone.Model.extend;

和:

Panel.extend = advancedExtend;

我的advancedExtend方法如下所示:

function advancedExtend(protoProps, classProps) {

    var delegate = this.render ? 
                        Backbone.View : (this.save ? 
                            Backbone.Model : (this.navigate ? 
                                Backbone.Router : Backbone.Collection)); 

    var child = delegate.extend.apply(this, [ protoProps, classProps ]);

    _.defaults(child.prototype.defaults, this.prototype.defaults);
    _.defaults(child.prototype.events, this.prototype.events);
    _.defaults(child.prototype.attributes, this.prototype.attributes);

    return child;
};

NB 我意识到 的定义delegate是没有意义的,因为所有的 Backbone 原型extend方法都是相同的,但认为这对于未来的版本可能会更健壮。

请参阅此处的 JSFiddle 。

我很想听听 Backbone 资深人士对这种方法的看法,因为它是非标准的......

于 2012-10-08T09:12:12.480 回答
1

问题是 JavaScript 没有经典继承,所以你需要在这里做一些特别的事情。

当您使用_.extend列表中距离您正在扩展的对象“右侧”最远的对象时,将在任何名称冲突中“获胜”。

您需要做的是覆盖扩展的定义以处理这些情况,或者将您的defaults哈希命名为类似local_defaults并将它们合并到您的initialization方法中

    initialize: function(options){
        var attrs = _.extend({}, this.defaults, this.local_defaults);
        this.set(attrs, {silent: true});
    },

类似的东西。您必须将属性散列设置为默认值集;通常这发生在模型的构造函数中,但模型只知道此时的默认值。

当然,如果您在 local_defaults 中重新定义默认值,您仍然会覆盖,因此您会考虑覆盖扩展。(我们已经为工作中的一些特定案例做了这些,如果您需要,我们很乐意为您提供帮助。)

于 2012-10-01T12:40:52.543 回答
1

有几种方法可以做到这一点,但在我的应用程序中,首选方法是将模型上的“默认值”定义为函数,因此您可以按照以下方式进行操作:

var Panel = Backbone.Model.extend({
    defaults : {
        name : 'my-panel'
    }
});

刚刚定义了您的扩展模型:

var AdvancedPanel = Panel.extend({
    defaults: function() {
        var d = _.result(Panel.prototype, 'defaults'); // doing this as top level might be a function as well
        return _.extend({}, d, {
            label : 'Click to edit' 
        });
    }
});

然后在控制台中你应该得到正确的 name 值

var advancedPanel = new AdvancedPanelModel();
console.log('advancedPanel', advancedPanel.get('name'));

这应该可以解决问题

于 2012-10-01T12:50:27.460 回答