【问题标题】:Backbone inheritance - deep copying骨干继承——深拷贝
【发布时间】:2012-10-01 10:57:56
【问题描述】:

我在 Backbone 中看到过这个关于继承的问题: Backbone.js view inheritance。有用但不能回答我的问题。

我遇到的问题是这样的:

假设我有一个班级Panel(本例中为model);

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 继承中获得的常见事情,有没有标准的做法?

【问题讨论】:

  • 我尝试了这里的建议:stackoverflow.com/questions/6549149/… 在这个小提琴中:jsfiddle.net/vesteraas/EXbnb
  • 啊,谢谢,我没有看到这个答案/问题,但现在我看到它是一个链接的。这很有用,虽然还不是很干净……我觉得既然“默认”是标准的 Backbone 模型属性,它应该由扩展来处理。我猜不是这样。还有其他建议吗?

标签: javascript inheritance backbone.js


【解决方案1】:

建立原型链的Backboneextend函数, 除其他外,它是通用的,用于视图、路由器、模型、 和收藏。所以它不知道像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 包括baseextended 和 传递给实例化的任何属性:

console.log(JSON.stringify(new Extended({ test: 'Hey' }).toJSON()));
>> {"base":123,"extended":456,"test":"Hey"}

【讨论】:

    【解决方案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;
    };
    

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

    请参阅 JSFiddle here

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

    【讨论】:

      【解决方案3】:

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

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

      这应该可以解决问题

      【讨论】:

      • 谢谢,我想这就是我应该做的。虽然我仍然有一点问题,但也许我对 Backbone 的期望过高......在我看来,如果 Backbone 在模型(和其他)上提供 extend 函数,那么标准模型属性应该在继承中也要考虑。
      • 如果您不想在每个模型实例化时调用默认函数,您也可以在定义AdvancedPanel 之后使用AdvancedPanel.prototype.defaults = _.extend(Panel.prototype.defaults, { **advanced defaults here** }); 之类的东西初始化高级默认值。如果您想扩展 events 哈希值,您也可以这样做。
      【解决方案4】:

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

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

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

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

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

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

      【讨论】:

      • 这真的有用吗?在调用initialize 函数时,我认为属性(以及用于脏跟踪的previousAttributes)已经从默认值与实际数据一起设置,您在每次实例化时在上面的代码中覆盖这些数据。
      • @Greg Argh,抱歉打字太快了。更新到我们在生产代码中使用的内容。类似,但略有不同。使用set,而不是直接修改this.attributes。但是,是的,我们现在在生产中使用它,所以它确实有效:)
      • 这里不想挑剔,但在调用初始化时,传递给构造函数的任何数据属性都已经在模型上设置。那时设置默认值,不会覆盖这些属性吗? new AdvancedPanel({ name: "A", label: "B"}) 会一直被默认值覆盖吗?
      • @Greg 是的。但是由于在通过构造函数设置值时不会发生验证,因此以这种方式设置它们是个坏主意。如果你不能验证你的数据,那总是一个大问题。看到这个答案:stackoverflow.com/questions/12658441/…
      • 我同意从用户那里收集信息时,您应该使用set(实际上我昨天赞成您的链接答案),但是当从已经验证数据的服务器获取集合时, Backbone.Collection 使用构造函数来实例化模型,其中属性直接传递给构造函数。在initialize 中设置默认值将覆盖这些属性。
      猜你喜欢
      • 2012-04-12
      • 1970-01-01
      • 2015-01-13
      • 2011-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-28
      • 2012-04-13
      相关资源
      最近更新 更多