随着 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 扩展语法的明显妥协,如果可以开发出更好的解决方案,那就太好了。 我不完全确定这应该是什么样子,但是在我为我的博客撰写文章时想到的一个想法是添加一个“属性”函数,该函数将输出属性的散列。 然后构造函数可以运行该函数并将它们添加到实例中,然后再由构造函数完成其他处理。
是的,这绝对是一个无赖。 谢谢你的跑腿工作。
我想这个故事的寓意是不要将 ES6 类与 Backbone 一起使用,至少在静态属性支持落地之前。 在您提出的后备选项中,我的首选解决方案是将字符串/对象定义为返回值。 Backbone 的 API 设计的一个关键部分是在这些原型共享的字符串和对象中,它会弄脏 API 以要求开发人员将每个属性分配给构造函数中的实例(更不用说浪费内存了)。
除了一致性之外,是否有任何理由在extend
上使用带有 Backbone 的 class 关键字?
很棒的博文。 我一直想知道 ES6 和 Backbone 类如何一起玩。 至于你的解决方案:
我想这个故事的寓意是不要将 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 是该主题的最后一句话? 谢谢。
谢谢。 我将尝试更长时间的 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?
@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 才能被视为完全支持):
我仍然认为所有这些都在某种程度上限制了表达能力。 但我认为如果装饰器成为官方规范,这个问题或多或少会得到解决。 对于装饰器,还有 2 个选项。
我认为这些解决方案中的任何一个都不需要对 #3684 以外的 Backbone 进行任何额外的修改,但是如果/当装饰器变得标准化时,主干装饰器库将会有一个有趣的角色。
在我周一/周二发布之前,希望对这篇文章有任何反馈。
@benmccormick我认为在任何构造发生之前都会评估装饰器,感谢您的更正。 我稍后会更新要点。 另外:感谢一百万在博客文章中的提及:) 当你发布它时在 twitter 上 ping 我? :+1:
我们可以对 Marionette 中的modelEvents
和collectionEvents
使用相同的语法,只是不适用于触发器。 这些可以通过类装饰器(如您博客文章中的 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示例。 它依赖于实验性的东西,但有效。
@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> {}
显式属性注释示例:
@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 而不是复制对每个新构造函数的引用。
最有用的评论
通读这篇文章,我发现https://github.com/epicmiller/es2015-default-class-properties是一个很好的方法。 在尝试时,我意识到 Backbone 对此具有内置支持。 例如:
上面的代码将正确设置 MyModel.prototype.idAttribute。 请注意,对于 TypeScript,需要稍微调整声明文件以返回构造函数接口,但这是与 ES6 用户无关的细节......