骨干继承 - 深度复制

时间:2012-10-01 10:57:56

标签: javascript inheritance backbone.js

我在Backbone中看到过有关继承的问题: Backbone.js view inheritance。有用,但没有回答我的问题。

我遇到的问题是:

说我有一个课程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/

我想我可以看到我可以通过创建原型的深层副本的一些自定义 extend 函数来实现这一目标,但这似乎是人们可能想要的Backbone继承,有没有一种标准的方法呢?

4 个答案:

答案 0 :(得分:3)

设置原型链的Backbone extend函数, 除其他外,是通用的,用于视图,路由器,模型, 和收藏品。所以它不知道像defaults这样的细节 或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包含baseextendedconsole.log(JSON.stringify(new Extended({ test: 'Hey' }).toJSON())); >> {"base":123,"extended":456,"test":"Hey"} 传递给实例化的任何属性:

{{1}}

答案 1 :(得分:2)

提到这一点,我写了一个简单的advancedExtend方法来替换该行(来自this inheritance construct):

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

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

请参阅JSFiddle here

我很想知道Backbone老兵对这种方法的看法,因为它是非标准的......

答案 2 :(得分: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中的默认值,你仍然会覆盖,所以你会考虑覆盖扩展。 (我们已经为这里的一些具体案例做了很多工作,如果你需要的话,很乐意提供帮助。)

答案 3 :(得分: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' 
        });
    }
});

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

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

这应该可以解决问题