Backbone: 主干和 ES6 类

创建于 2015-04-07  ·  63评论  ·  资料来源: jashkenas/backbone

随着 ES6 类规范的最终更改(详情请点击此处),不再可能将 ES6 类与 Backbone 一起使用,而不会在语法方面做出重大妥协。 我写的情况充分说明这里(请务必通过点击底部的评论额外减轻选项),但本质上是没有办法的属性之前,父母子类添加到子类的实例正在运行的构造函数。

所以这:

class DocumentRow extends Backbone.View {

    constructor() {
        this.tagName =  "li";
        this.className = "document-row";
        this.events = {
            "click .icon":          "open",
            "click .button.edit":   "openEditDialog",
            "click .button.delete": "destroy"
        };
        super();
    }

    initialize() {
        this.listenTo(this.model, "change", this.render);
    }

    render() {
        //...
    }
}

在最终的 ES6 规范中不再有效。 相反,如果您想尝试使这项工作有效,您实际上有 3 个(不是很吸引人的)选项:

将所有属性附加为函数

Backbone 允许这样做,但写这样的东西感觉很愚蠢:

class DocumentRow extends Backbone.View {

    tagName() { return "li"; }

    className() { return "document-row";}

    events() {
        return {
            "click .icon":          "open",
            "click .button.edit":   "openEditDialog",
            "click .button.delete": "destroy"
        };
    }

    initialize() {
        this.listenTo(this.model, "change", this.render);
    }

    render() {
        //...
    }
}

与当前的扩展语法相比

两次运行构造函数

我不认为这是一个真正的选择,因为它会导致使用不同的 cid 再次运行初始化等问题。

将所有属性作为默认选项传递给超类构造函数

这是我博客上的一位评论者建议的,可能是当前最实用的选项。 它看起来像这样:

class MyView extends Backbone.View {
  constructor(options) {
    _.defaults(options, {
      // These options are assigned to the instance by Backbone
      tagName: 'li',
      className: 'document-row',
      events: {
        "click .icon": "open",
        "click .button.edit": "openEditDialog",
        "click .button.delete": "destroy"
      },
      // This option I'll have to assign to the instance myself
      foo: 'bar'
    });


    super(options);


    this.foo = options.foo;
  }
}

由于所有这些当前选项都涉及相对于当前 Backbone 扩展语法的明显妥协,如果可以开发出更好的解决方案,那就太好了。 我不完全确定这应该是什么样子,但是在我为我的博客撰写文章时想到的一个想法是添加一个“属性”函数,该函数将输出属性的散列。 然后构造函数可以运行该函数并将它们添加到实例中,然后再由构造函数完成其他处理。

change

最有用的评论

通读这篇文章,我发现https://github.com/epicmiller/es2015-default-class-properties是一个很好的方法。 在尝试时,我意识到 Backbone 对此具有内置支持。 例如:

class MyModel extends Backbone.Model.extend({
   idAttribute: 'id'
}) {
   // ...
};

上面的代码将正确设置 MyModel.prototype.idAttribute。 请注意,对于 TypeScript,需要稍微调整声明文件以返回构造函数接口,但这是与 ES6 用户无关的细节......

所有63条评论

是的,这绝对是一个无赖。 谢谢你的跑腿工作。

我想这个故事的寓意是不要将 ES6 类与 Backbone 一起使用,至少在静态属性支持落地之前。 在您提出的后备选项中,我的首选解决方案是将字符串/对象定义为返回值。 Backbone 的 API 设计的一个关键部分是在这些原型共享的字符串和对象中,它会弄脏 API 以要求开发人员将每个属性分配给构造函数中的实例(更不用说浪费内存了)。

除了一致性之外,是否有任何理由在extend上使用带有 Backbone 的 class 关键字?

很棒的博文。 我一直想知道 ES6 和 Backbone 类如何一起玩。 至于你的解决方案:

  1. 将所有属性附加为函数:我并不反对这一点。 它不像直接在原型上设置对象那么干净,但我已经看到大量代码在改变原型对象时出错。 这种方式是免疫的,这就是为什么我认为 ES6 选择不包含类属性的原因。
  2. 将所有属性作为默认选项传递:这不是您用更经典的语言做某事的方式吗? 我觉得这是一个比上述更不干净的解决方案。
  3. 运行构造函数两次:Ick。

我想这个故事的寓意是不要将 ES6 类与 Backbone 一起使用,至少在静态属性支持落地之前。

甚至类属性也出现在super()调用之后。 :失望的:

除了一致性之外,是否有任何理由将 class 关键字与 Backbone 一起使用?

我在博客文章中解决了这个问题。 几乎? 不。从理论上讲,从长远来看,它可以让 Backbone 减少代码和额外的概念,但实际上它至少需要几年时间才能在所有相关浏览器上广泛支持 ES6 类而无需转译,而代码减少将是下一个一无所有。

但不要低估一致性方面。 如果这成为在 JavaScript 中进行面向对象编程的“方式”(考虑到 Ember/Angular/React/Typescript/Aurelia 等对此的标准化),Backbone 不使用它将是该库相对于其他选项。 特别是对于初级开发人员。 我不确定这是否一定值得改变。 但这不仅仅是为了学究气的“小精灵”的一致性。

我同意@akre54@jridgewell 的观点,即“将所有属性附加为函数”方法可能是建议选项中最好的。 FWIW,我记得当我最初作为一个相对的js新手学习backbone时,我对这些“静态”属性以及应该如何使用它们有点困惑。

ES7 将具有正确的类属性,我猜https://gist.github.com/jeffmo/054df782c05639da2adb

ES7 提案就是这样,一个非常早期的社区驱动提案。 完全不清楚它实际上是否会成为官方规范的一部分。 当前的实现会导致在构造函数运行后将属性添加到实例中,因此它对 Backbone 没有帮助。 (请参阅上面 jridgewell 的链接或使用 Babel 5.0.0 自己尝试)

@jridgewell我指的是@benmccormick帖子的这一部分:

React 开发人员已经注意到 Backbone 用户遇到的与属性初始化器相同的问题。 作为 React 0.13 版本的一部分,他们支持类的特殊属性初始化语法,最终可能会标准化。 在这个 ESDiscuss thread 中有更多信息。 该标准仍在制定中,但在 Babel 5.0.0 中提供了一个实验性支持版本。 不幸的是,该版本将类属性定义为在超类构造函数运行后实例化,因此这并不能解决 Backbone 的问题。

例如,请参阅wycats 的 js-decorators 稻草人或原始(已取代)和声类提案

我可能会建议我们使用具有类属性的 getter:

class Row extends Backbone.View {
  get tagName() { return 'li'; }
}

作为绝对的最后手段,我们可以使用帮助程序 a la _.result检查实例或静态道具:

_.instOrStaticVar = function(instance, property) {
  if (instance == null) return void 0;
  var value = instance[property] || instance.constructor[property];
  return _.isFunction(value) ? value.call(instance) : value;
}

是的,但是:

不幸的是,该版本将类属性定义为在超类构造函数运行后实例化,因此这并不能解决 Backbone 的问题。

所以,ES5'd:

// ES6
class View extends Backbone.View {
  tagName = 'li';

  constructor() {
    // Do anything that doesn't touch `this`
    super();
    // Do anything that touches `this`
  }
}

// ES5
function View() {
  // Do anything that doesn't touch `this`
  Backbone.View.apply(this, arguments);

  // Add class properties
  this.tagName = 'li';

  // Do anything that touches `this`
}
View.prototype = _.create(Backbone.View.prototype, {
  constructor: View
});

在我们更改设置实例变量之前,我们的元素仍将被构造。

例如,请参见 wycats 的 js-decorators 稻草人...

你能解释一下装饰器是如何应用的吗?

我可能会建议我们使用具有类属性的 getter:

:+1:. 我认为这与将所有属性附加为函数一样。 不像我们目前拥有的那么干净,但完全可以接受并且可以防止突变。

作为绝对的最后手段,我们可以使用助手 a la _.result 检查实例或静态道具:

那可能很有趣……

你可以这样做:

class MyView extends Backbone.View {
  constructor() {
    super({ tagName: 'h1' });
    this.el.textContent = 'Hello World';
  }
}

@thejameskyle那是将所有属性作为默认选项传递给超类构造函数选项。 :stuck_out_tongue:

您可以简单地使用init()函数或其他东西,而不是依赖super()来设置类。

class DocumentRow extends Backbone.View {

    constructor() {
        super();
        this.tagName =  "li";
        this.className = "document-row";
        this.events = {
            "click .icon":          "open",
            "click .button.edit":   "openEditDialog",
            "click .button.delete": "destroy"
        };
        this.init();
    }

    initialize() {
        this.listenTo(this.model, "change", this.render);
    }

    render() {
        //...
    }
}

@milesj嗯? 最终的 ES6 类规范会立即出错

在派生类中,必须先调用 super() 才能使用 this

即使它确实有效,您也不会真正调用 Backbone 构造函数,也不会获得其初始化代码。

从我的第一篇文章中看到这个链接: http :

@milesj :问题是,您必须在设置this.tagName之前调用super()等。 而且,由于我们确保在 View 的构造函数中有一个元素,因此在设置this.tagName之前我们已经创建了一个元素。

@milesj在您进行子类化时仍然不允许。

@jridgewell哦对不起,我错过了。 这似乎是最自然的选择。 我和 jeffmo 和 sebmck 谈过这件事。

给你们一些背景故事,原因是为了支持扩展本机类型(即数组),直到您调用super()方法才确定this 。 否则,您会在 DOM(以及其他地方)中遇到初始化问题。

@jridgewell @thejameskyle然后只需先调用 super() (更新示例)。 我真的没有看到这里的问题,因为我在 ES6 类中做了同样的事情。 只需将视图构造函数逻辑移至init()方法即可。

这是很多非常昂贵的代码运行两次。

@milesj你读过原博文吗? 首先运行 super 意味着不处理属性。 在这里查看完整的深入解释: http :

是的,我读过它,我仍然很好奇为什么这不是解决方案。 每个人都在谈论需要调用的视图构造函数,但事实并非如此。 为什么像下面这样的东西不是解决方案(尽管有点做作)?

var View = Backbone.View = function(options) {
    this.cid = _.uniqueId('view');
    // extend()ing options is no longer needed if properties are set directly
};

View.prototype.setup = function() {
    this._ensureElement();
    this.initialize.call(this, arguments);
};

class DocumentRow extends Backbone.View {
    constructor() {
        super();
        this.tagName =  "li";
        this.className = "document-row";
        this.events = {
            "click .icon":          "open",
            "click .button.edit":   "openEditDialog",
            "click .button.delete": "destroy"
        };
        this.setup(...arguments);
    }
}

我猜是因为向后兼容非 ES6?

然后默认的View类将不起作用,因为构造函数从不调用#setup 。 而且,强制子类调用除super()内容都会非常烦人。

这是所有 ES6 类都必须处理的问题,而不仅仅是 Backbone。 我个人通过使用 Babel ES7 类属性规范解决了它。

@milesj如前所述,ES7 类属性不能解决这个问题,因为它们直到构造函数结束时才被实例化。

我与 jeffmo 和 sebmck 谈过这样做:

class Root {
  rootProp = 'root';
  constructor() {
    console.log('Root', this.rootProp);
    console.log('Root', this.derivedProp);
  }
}

class Derived extends Root {
  derivedProp = 'derived';
  constructor() {
    super();
    console.log('Derived', this.rootProp);
    console.log('Derived', this.derivedProp);
  }
}

脱糖:

function Root() {
  this.rootProp = 'root';
  console.log('Root', this.rootProp);
  console.log('Root', this.derivedProp);
}

function Derived() {
  super();
  this.derivedProp = 'derived';
  console.log('Derived', this.rootProp);
  console.log('Derived', this.derivedProp);
}

但这仍然不能解决这里的问题并导致不一致:

new Derived();
// >> 'Root' 'root'
// >> 'Root' undefined
// >> 'Derived' 'root'
// >> 'Derived' 'derived'

这是所有 ES6 类都必须处理的问题,而不仅仅是 Backbone。

嗯?

我个人通过使用 Babel ES7 类属性规范解决了它。

你会有很多没有className的 DIV 元素。 见最后一点https://github.com/jashkenas/backbone/issues/3560#issuecomment -90739676, https://github.com/jashkenas/backbone/issues/3560#issuecomment -91601515 和https://github .com/jashkenas/backbone/issues/3560#issuecomment -98827719。

我知道了。 在这种情况下,我建议使用“将所有属性作为默认选项传递给超类构造函数”选项,或关于创建“属性”方法(不涉及构造函数)的最后一行。

class DocumentRow extends Backbone.View {
    loadProperties() {
        return {
            tagName: 'li',
            className: 'document-row',
            events: {
                "click .icon": "open",
                "click .button.edit": "openEditDialog",
                "click .button.delete": "destroy"
            },
            foo: 'bar'
        };
    }
}

// Contrived example
var View = Backbone.View = function(options) {
    this.cid = _.uniqueId('view');
    options || (options = {});
    _.extend(this, this.loadProperties(), _.pick(options, viewOptions));
    this._ensureElement();
    this.initialize.apply(this, arguments);
};

我在 Toolkit 中做了类似的事情,可以在这里看到: https :

你好。

如果我正确理解这里的讨论 - 主干开发人员正在讨论变通方法和最佳实践,但无意对 BB 核心进行实际更改以处理此问题? (我不是建议他们应该这样做,我也不知道这些变化会是什么)。 换句话说,是否建议将所有属性用作函数或 getters 是该主题的最后一句话? 谢谢。

@gotofritz我们正在讨论解决方法,因为 ES6 强制所有属性存在于实例上的解决方案无法扩展。 Backbone 的类系统在这里做正确的事情。

一些关于ES7 类讨论,但到目前为止还没有具体的讨论。 与此同时,我会说坚持使用 Backbone 的extend

谢谢。 我将尝试更长时间的 ES6 类... :-)

为了让其他遇到此问题的人受益,在实践中我发现“将所有属性作为默认选项传递给超类构造函数”更好 - 例如,我们的应用程序具有需要在实例化时传入的动态(本地化)路由,并且有一个 routes() 方法是行不通的。 而以下内容

class Router extends Backbone.Router {

 constructor (localizedRoutes) {
    _.defaults(localizedRoutes, {
        "nonLocalizedRouteA/": "routeA"
        "*actions": "defaultRoute"
     });
 super({ routes: localizedRoutes });
}

我刚刚看过这个,我认为这两种解决方法都不适用于 Model 具有的idAttribute属性。 一种方法将不起作用,因为 Backbone 使用model.idAttribute访问该属性; 并且模型构造函数似乎完全不支持将属性添加为选项。

我认为这两种解决方法都不适用于 idAttribute 属性

出色的捕获,我将致力于解决此问题的 PR。 同时,您可以使用 getter 符号来提供自定义的idAttribute (和cidPrefix ):

class Model extends Backbone.Model {
  get idAttribute() {
    return '_id';
  }

  get cidPrefix() {
    return '__c';
  }
}

方法将不起作用,因为 Backbone 使用 model.idAttribute 访问该属性

get idAttribute() { return '_id'; }是一个 getter 方法,它的访问方式与普通属性一样。 this.idAttribute === '_id';

这听起来像是需要进行重大重写。 可能是 Backbone v2?

这听起来像是需要进行重大重写。 可能是 Backbone v2?

完全没有,我们已经支持 ES6 子类化(除了Models )。 我认为如果有人探索@akre54静态属性建议会很有趣,但即使这对于原始帖子中的两个解决方案也没有必要。

@jridgewell ,非常感谢您的快速解决方案!

上面这个帖子中提到了装饰器(特别是Yehuda Katz 的提议),是否可以解决这个问题尚未解决。

我只是按照建议玩弄它们,您可以编写这样的装饰器:

function props(value) {
    return function decorator(target) {
        _.extend(target.prototype, value);
    }
}

然后我们一直在使用的例子可以这样写

@props({
      tagName: 'li',
      className: 'document-row',
      events: {
        "click .icon": "open",
        "click .button.edit": "openEditDialog",
        "click .button.delete": "destroy"
      }
    })
class DocumentRow extends Backbone.View {

    initialize() {
        this.listenTo(this.model, "change", this.render);
    }

    render() {
        //...
    }
}

这对我来说似乎很好用。 在执行类构造函数之前,装饰器应用于类。 这只是声明性的说法

class DocumentRow extends Backbone.View {

    initialize() {
        this.listenTo(this.model, "change", this.render);
    }

    render() {
        //...
    }
}
_.extend(DocumentRow.prototype, {
      tagName: 'li',
      className: 'document-row',
      events: {
        "click .icon": "open",
        "click .button.edit": "openEditDialog",
        "click .button.delete": "destroy"
      }
})

实际上,我还没有测试过它,但是如果您同时需要静态和原型道具,您可能可以将整个主干扩展功能作为装饰器。

不幸的是,这只是目前的一个提议,但 Babel 在实验性标志背后支持它,所以如果人们喜欢冒险,这是一个可能的解决方案。

@benmccormick ,装饰器技术对我来说效果很好。 除了这只是目前的一个建议之外,是否还有其他关于采用这种方法的担忧?

@andrewrota我现在正在写一篇博客文章来跟进这些东西(当你评论时正在阅读这个线程)。 这是一个很大的“其他”,但我个人没有看到任何。 我实际上认为我们可以做得比我上面描述的更好,并为带有装饰器的 Backbone 创建一些不错的新接口。

@StevenLangbroek看到这个让我想到这个的要点: https : //gist.github.com/StevenLangbroek/6bd28d8201839434b843

这是我发布的后续帖子的预览: http : 现已更新永久链接

它会在本周初某个时候移至永久网址。 但是这个线程的基本摘要和我学到的东西是:

有 3 种方法可以使 Backbone 属性与当前的 ES6 类规范一起使用(前两种需要 #3684 才能被视为完全支持):

  1. 将所有属性传递给构造函数中的 super
  2. 将所有属性视为方法
  3. 类被声明后,直接在原型中添加属性

我仍然认为所有这些都在某种程度上限制了表达能力。 但我认为如果装饰器成为官方规范,这个问题或多或少会得到解决。 对于装饰器,还有 2 个选项。

  1. 添加一个 props 装饰器,将类顶部的 props 添加到原型中
  2. 创建几个特殊用途的装饰器,以实现更具表现力/细粒度的界面。

我认为这些解决方案中的任何一个都不需要对 #3684 以外的 Backbone 进行任何额外的修改,但是如果/当装饰器变得标准化时,主干装饰器库将会有一个有趣的角色。

在我周一/周二发布之前,希望对这篇文章有任何反馈。

@benmccormick我认为在任何构造发生之前都会评估装饰器,感谢您的更正。 我稍后会更新要点。 另外:感谢一百万在博客文章中的提及:) 当你发布它时在 twitter 上 ping 我? :+1:

我们可以对 Marionette 中的modelEventscollectionEvents使用相同的语法,只是不适用于触发器。 这些可以通过类装饰器(如您博客文章中的 tagName 和模板)公开,但我在想:我们不能为此使用静态属性吗? 或者这在 Backbone 中不起作用?

我知道装饰器仍然是第 0 阶段,但我认为它们将是我们编写 Backbone 应用程序的方式的一个很好的升级,尤其是事件哈希上的方法装饰器,这种编程风格也让我更喜欢 gulp 而不是 grunt。

@StevenLangbroek有关静态属性的讨论,请参见上文。

当前指定的语法在每个实例上创建一个本地属性,而不是添加到原型中。 这些属性是在超级构造函数运行后添加的。

@benmccormick

@benmccormick装饰器是否应该使用构造函数而不是原型调用_#extend ,然后使用@akre54_.instOrStaticVar方法代替_#result ? 我意识到这将是一个重大变化,但恕我直言,这样看起来更干净。 就像@akre54指出以这种方式定义的属性是原型共享的字符串和对象(即在所有实例之间共享),因此它们应该通过类访问,对吗?

我会更进一步,按照我们需要的方式制作工作类属性。 类属性也可以被注释,我们可以创建特殊的装饰器,将被装饰的属性附加到原型上。

class TodoView extends Backbone.View {
  <strong i="6">@protoprop</strong>
  static tagName = 'li';
}

function protoprop(target, name, descriptor) {
  target.prototype[name] = descriptor.initializer()
}

参见Babel REPL示例。 它依赖于实验性的东西,但有效。

@just-boris 在我的博客评论中讨论过,你在那里看到的行为是 Babel 处理类属性和装饰器规范的实现细节。 目前在任何提案中都没有定义它的行为。 如果您想以这种方式做事,您需要在此处和/或此处提出问题以使类属性上的装饰器成为标准化行为。 否则你正在做的事情可能(并且可能会)随时中断。

@benmccormick wycats/javascript-decorators已经有关于属性初始值设定项的额外定义

那里的主要问题是属性初始化器是一个常用的描述符,以及类方法,因此装饰器也可以包装它们。 我认为没有理由担心,而该部分中的规范保持不变

啊,太酷了,我没看过。 感谢您指出了这一点。

2015 年 9 月 21 日星期一上午 11:29,Boris Serdiuk通知@github.com
写道:

@benmccormick https://github.com/benmccormick
https://github.com/wycats/javascript-decorators已经有额外的
关于属性初始值设定项的定义
https://github.com/wycats/javascript-decorators/blob/master/INITIALIZER_INTEROP.md
.

那里的主要问题是属性初始值设定项是一个常用的描述符,
以及类方法,因此装饰器也可以包装它们。 我没看见
有理由担心,而该部分中的规范保持不变


直接回复此邮件或在 GitHub 上查看
https://github.com/jashkenas/backbone/issues/3560#issuecomment -142015454
.

只是想知道使用https://github.com/typhonjs/backbone-es6与@benmccormick 建议的方法技术的优缺点。

顺便说一句,感谢@benmccormick的精彩博文!

除了此处附加的提案 (#121) 拉取请求properties方法https://github.com/dsheiko/backbone-abstract/tree/master/demo-es6/src/Js
正如@akre54提到的,贾斯汀已经提出了一个类似的解决方案( preInitialize方法)。 虽然已经在我的分支上使用它,但它确实解决了我的问题。 尽管 TypeScript 不禁止声明性类属性,但它们似乎在 TypeScript 中也很有用。

PS preInitialize听起来更一般,因此在这种情况下更好。 尽管如此,如果我们在所有构造函数的工作之前调用该方法,它更像是preConstruct

真的希望我们能看到一个新的类属性提案,将它们设置在原型上。 似乎许多参与提案的人都担心其含义,但我发现类方法直接附加到原型上是非常不一致的,而 jeffmo 的提案将它们放在构造函数中。

如果他们直接将属性附加到原型上,您几乎可以将任何 React/Backbone 代码迁移到 ES2015 类。

很棒的博文@benmccormick !! 将在我的项目中使用这些装饰器

@benmccormick ,我想出了另一种方法来声明具有默认属性的类,看看: https :

它在本机支持类的任何环境中正常运行,转换良好,并且看起来比在构造函数中或在声明之后定义它们更好。 随着 ES2016/ES2017 的装饰器和类属性提案的推出,这可能更像是一种学术练习,而不是 Backbone 的长期解决方案,但如果 2-3 年太长,这样的事情绝对是一个可行的选择等待。

好吧,问题是 Class Properties 在 Ecmascript 提案阶段系统中仍处于阶段 1。 我不知道为什么,因为就“用户得到的东西”而言,这似乎是一个噱头。 当然,我不知道它在语法和参考实现方面可能会破坏什么样的东西。

https://github.com/tc39/ecma262
https://github.com/jeffmo/es-class-fields-and-static-properties

通读这篇文章,我发现https://github.com/epicmiller/es2015-default-class-properties是一个很好的方法。 在尝试时,我意识到 Backbone 对此具有内置支持。 例如:

class MyModel extends Backbone.Model.extend({
   idAttribute: 'id'
}) {
   // ...
};

上面的代码将正确设置 MyModel.prototype.idAttribute。 请注意,对于 TypeScript,需要稍微调整声明文件以返回构造函数接口,但这是与 ES6 用户无关的细节......

@t-beckmann 这是一个非常好的解决方案 - 看起来可读并且需要最少的更改。 谢谢!

我意识到这个线程已经持续了 2 年,但它仍然是搜索 Backbone 和 ES6 类时最好的(也是唯一的)结果之一,我想我会分享一个潜在的解决方案,利用这里多次提到的类属性.

既然类属性处于第 2 阶段,并且可以通过 babel 预设广泛使用,我想我会再看看它。 如前所述,实例/成员属性的问题在于它们直到 _after_ constructor()才会应用于原型,但是许多需要设置的属性在构造函数中使用。 静态属性会立即应用,但(按设计)不会复制到类的实例中。

下面的 shim 在运行构造函数之前将静态属性从构造函数复制到实例上(有效地创建一个新的构造函数,应用属性,然后执行原始构造函数)。 虽然这绝对是一个 hack,但我对结果非常满意:

垫片:

export default function StaticShim(Ctor) {
    const NewCtor = function shim(...args) {
       Object.keys(Ctor).forEach((key) => {
            if (this[key] === undefined) {
                this[key] = toApply[key];
            }
        });

        Object.assign(this, this.constructor);

        Ctor.apply(this, args);
    };

    NewCtor.prototype = Object.create(Ctor.prototype);
    NewCtor.prototype.constructor = NewCtor;

    Object.keys(Ctor).forEach((key) => {
        if (NewCtor[key] === undefined) {
            NewCtor[key] = Ctor[key];
        }
    });

    return NewCtor;
}

然后在使用中:

class TestModel extends StaticShim(Backbone.Model) {
    static idAttribute = '_id';
    static urlRoot = '/posts';

    initialize() {
        console.log(this.url()); // Correctly logs "/posts/{id}"
    }
}

只是想把它放在这里,以防它对其他人有帮助,或者任何人对此有任何想法。 谢谢!

必须抱歉恢复旧问题。

编写一个将 ES6 类声明转换为使用 Backbone.*.extend({...}) 的 babel 插件是否可能或值得?

@enzious似乎绝对有可能。 值不值得看你的:)

@t-beckmann 的解决方案似乎是最直接的。 我们应该将它整合到主干本身吗?

对我来说它看起来不正确,有一个设置 idAttribute 的方法不是更合适吗?

此外,如果有 Promise 支持,那就太棒了。 这是一种比使用 jquery Deferred 更原生的方法,我个人希望看到在 Backbone 中弃用它。

这里的故事仍然很不清楚刷新遗留的 Backbone 应用程序以利用现代工具和语言功能。 看到 Symbol.iterator 之类的东西被实现但在生产版本中不可用,尤其令人失望。

对于那些仍在寻找这个问题更清晰答案的人,我将 TypeScript 添加到主干应用程序中,并发现此评论中的解决方案最有帮助。

到目前为止,它工作得足够好,缺点是必须显式注释通过装饰器传递的属性而不是更好的推理。

export function Props<T extends Function>(props: { [x:string]: any }) {
  return function decorator(ctor: T) {
    Object.assign(ctor.prototype, props);
  };
}

@Props({
  routes: {
    home: "home",
    about: "about",
    dashboard: "dashboard",
    blog: "blog",
    products: "products",
    accountSettings: "accountSettings",
    signOut: "signOut",
  },
})
export class Router extends Backbone.Router {
  home() {}
  about() {}
  // ...
}

@Props({
  model: CategoryModel,
  comparator: (item: CategoryModel) => item.display_value,
})
export class CategoryCollection extends Backbone.Collection<CategoryModel> {}

显式属性注释示例:

image

@raffomania@jridgewell & Co. 无论如何,我的团队通过将 idAttribute 添加到类外的原型来解决这个问题。

类示例扩展父示例{
// 类方法等在这里
}

x.Example = 示例;

x.Example.prototype.idAttribute = 'customIdAttr';

@kamsci我在这个分支中做了同样的事情,我将 Backbone 转换为 ES6 类

Backbone 使用 _configuration_ 到配置对象是 _declarative_ 的点。 这很好,但它永远不会很好地继承继承。 (克隆类,然后配置它。那不是继承。)

如果我们要使用主干编写新代码,可以换个思路。 剪切和粘贴 ES5 代码,然后让它看起来像 ES6 不起作用。 所以呢?

通过配置对象传递所有内容我没有任何问题。 我们如何公开该配置的内容,或使其更易于阅读/使用,是一个需要解决的问题,而不是哭泣。

没有人想两次运行构造函数。 那是愚蠢的。 但是,图案

Foo = BackboneThing.extend({LONG DECLARATIVE OBJECT LITERAL}) 也是爱妈妈的丑陋。 你们都做了这么久,你没有看到它有多丑陋。

仅供参考:我有一个大型 Marionette 项目,想使用 ES6 语法。 我创建了一个 jscodeshift 转换器,将 Backbone 扩展声明转换为 ES6 类。 它做了许多简化的假设,但对于你们中的一些人来说可能仍然有用,如果只是作为一个起点。 当我遇到装饰器问题时,它遵循@t-beckmann 提出的语法。
https://gist.github.com/maparent/83dfd65a37aaaabc4072b30b67d5a05d

对我来说,这个线程中似乎用词不当。 ES6 的“静态属性”是构造函数上的属性,它们存在于没有实例化的类中(例如 Class.extend)。 在此线程中,“静态属性”似乎是指具有“静态”值(不是 getter 或函数)的原型上的命名属性。 我做对了吗?

对于具有静态值的原型属性,将 Backbone 预初始化值声明为函数返回值是一种非常简单的转换,并且效果很好,因为 _.result 对默认值、className、id 等执行预期。其他实例属性似乎可以很好地声明在初始化函数的顶部正常。 这个问题似乎只出现在 ES6 类中,您目前无法使用静态值定义原型属性,只能定义 getter、setter 和函数。

无论哪种方式,构造函数/类静态属性(Class.extend)都不会像在 ES6 中那样在主干中继承。 每次执行扩展功能时,Backbone 都会将类的静态属性复制到新的类/构造函数,而不是像 ES6 那样继承这些属性。 我在这里做了一个公关来解决这个问题https://github.com/jashkenas/backbone/pull/4235

我很感激一些评论/反馈,我不确定它是否会破坏任何东西,我已经对其进行了相当多的测试,它似乎运行良好。 骨干类之后继承 Class.extend 而不是复制对每个新构造函数的引用。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

cueedee picture cueedee  ·  3评论

jonathan picture jonathan  ·  11评论

miguelpayet picture miguelpayet  ·  9评论

tribalvibes picture tribalvibes  ·  11评论

rafde picture rafde  ·  9评论