ECMAScript 6 中较短的类初始化
Posted
技术标签:
【中文标题】ECMAScript 6 中较短的类初始化【英文标题】:A shorter class initialisation in ECMAScript 6 【发布时间】:2016-01-24 12:23:58 【问题描述】:每次我创建一些类时,我都需要做同样无聊的过程:
class Something
constructor(param1, param2, param3, ...)
this.param1 = param1;
this.param2 = param2;
this.param3 = param3;
...
有什么方法可以让它更优雅更短?我使用 Babel,所以允许一些 ES7 实验特性。也许装饰器可以提供帮助?
【问题讨论】:
需要是类吗?您可以使用速记对象 litteral 将参数分配为属性,而无需重复它们的名称。 回答你的最后一个问题,装饰器在这里无济于事,因为它们不能附加到class constructor
【参考方案1】:
你可以使用Object.assign
:
class Something
constructor(param1, param2, param3)
Object.assign(this, param1, param2, param3);
这是 ES2015(又名 ES6)的一项功能,可将一个或多个源对象的自己的可枚举属性分配给目标对象。
当然,你必须写两次 arg 名称,但至少它要短得多,如果你把它作为你的习惯用法,当你在实例上有你想要的参数和其他你不想要的参数时,它会很好地处理它t,例如:
class Something
constructor(param1, param2, param3)
Object.assign(this, param1, param3);
// ...do something with param2, since we're not keeping it as a property...
示例:(live copy on Babel's REPL):
class Something
constructor(param1, param2, param3)
Object.assign(this, param1, param2, param3);
let s = new Something('a', 'b', 'c');
console.log(s.param1);
console.log(s.param2);
console.log(s.param3);
输出:
一种 b C【讨论】:
这很简洁,但您仍在重复参数名称(一次在参数列表中,一次在分配调用中)。您可以克隆arguments
,删除预定义属性,然后将其传递给 assign
。
@Touffy:arguments
是否有参数的 named 属性?我的意思是,除了0
、1
和2
之外的名称?如果是这样的话,这对我来说是新的。
嗯,它没有。我想你可以很容易地从函数的 toString 中解析它们,但这越来越难看。
@Touffy:是的,这是一个很有用的习惯用法,即使您只需要将其中一些映射到对象上,您也可以继续使用它。
我要做的是将第一个参数视为属性映射,就像在 Chev 的最后一个解决方案中一样(当然,使用 Object.assign 而不是循环)。但是,如果您不相信调用代码会发送正确的名称,那么您的解决方案会更安全。【参考方案2】:
不幸的是,你所能做的只是简单的事情,比如Object.assign
,但如果你试图消除两次输入所有参数的冗余(一次在构造函数签名中,一次在赋值中),那么你就无能为力了做。
也就是说,您可以进行这样的破解。虽然我不确定随之而来的努力和一点点混淆是否值得。
var dependencies = ['param1', 'param2', 'param3'];
class Something
constructor(...params)
params.forEach((param, index) => this[dependencies[index]] = param);
var something = new Something('foo', 'bar', 'baz');
// something.param1 === 'foo'
这样,您将使用单个参数名称数组,然后在 Something
实例上创建属性时使用相同的数组作为引用。这种模式适用于 Angular 应用程序,您尝试通过设置 $inject
属性来通过缩小来保留依赖项名称。
Something.$inject = dependencies;
PS - 欢迎来到我成为 JS 开发人员时认为我已经摆脱的经典语言的冗余地狱:P
老实说,除非你真的需要实际类的形式,否则你应该只使用经典的对象字面量。
编辑:我想你可以在你的构造函数中接受一个对象文字,如果你想要一个简单的文字和一个实际类的形式。
class Something
constructor(params)
Object.keys(params).forEach((name) => this[name] = params[name]);
var something = new Something(
param1: 'foo',
param2: 'bar',
param3: 'baz'
);
但是现在你刚刚将一个类变成了一个可以用任何属性实例化的动态类,有点像对象字面量:P
通常我想要一个类,因为我想形式化对象并提供一致且可严格测试的 API。
【讨论】:
这很难看,但会给你函数的参数名称,这样你就可以将它们写入函数定义而不是外部数组:arguments.callee.toString().match(/^function.*?\((.*?)\)/)[1].split(/,\s*/)
@Touffy 这对我不起作用。严格模式不允许我访问 arguments.callee
,因此您可能需要一个 further workaround 来获得该解决方案,使其更加丑陋。【参考方案3】:
我们可以在每个类中创建一个静态方法,该方法接受arguments
对象和一个名称数组,并返回一个可以使用Object.assign
分配给新实例的对象。
使用Babel REPL查看。
class Something
static buildArgs (ctx, args, paramNames)
let obj =
Array.from(args).forEach(function (arg, i)
let name = paramNames[i] || i
obj[name] = args[i]
)
Object.assign(ctx, obj)
constructor ()
Something.buildArgs(this, arguments, [
'param1',
'param2'
]);
console.log(this)
new Something('one', 'two')
诚然,添加一个方法buildArgs
意味着这个解决方案并不短,但是constructor
的主体是,我们也有这些优点:
-
您只需要编写一次参数名称。
您受到保护,不会被缩小。
上面的代码包含额外的参数 (i >= paramNames.length
),但是如果这种行为不受欢迎,我们可以修改它,以便这些仍然被解析,但没有分配给实例:
class Something
static buildArgs (ctx, args, paramNames)
let obj = instance: , extra:
Array.from(args).forEach(function (arg, i)
let name = paramNames[i] || i
if (name)
obj.instance[name] = args[i]
else
obj.extra[i] = args[i]
)
Object.assign(ctx, obj)
constructor ()
let args = Something.buildArgs(this, arguments, ['param1', 'param2']);
// Do stuff with `args.extra`
或完全忽略:
static buildArgs (args, paramNames)
let obj =
Array.from(args).forEach(function (arg, i)
let name = paramNames[i]
if (name) obj[name] = args[i]
)
return obj
【讨论】:
以上是关于ECMAScript 6 中较短的类初始化的主要内容,如果未能解决你的问题,请参考以下文章