ECMAScript 6 中更短的 class 初始化

A shorter class initialisation in ECMAScript 6

每次我创建一些 class,我都需要做同样无聊的过程:

class Something {
  constructor(param1, param2, param3, ...) {
    this.param1 = param1;
    this.param2 = param2;
    this.param3 = param3;
    ...
  }
}

有什么办法让它更优雅更短吗?我使用 Babel,因此允许使用一些 ES7 实验性功能。也许装饰器可以提供帮助?

您可以使用 Object.assign:

class Something {
  constructor(param1, param2, param3) {
    Object.assign(this, {param1, param2, param3});
  }
}

这是一项 ES2015(又名 ES6)功能,可将一个或多个源对象的 自己的可枚举 属性分配给目标对象。

当然,您必须将 arg 名称写两次,但至少它要短得多,并且如果您将此设置为您的惯用语,当您在实例上有您确实需要的参数和其他您想要的参数时,它会很好地处理它不要,例如:

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

输出:

a
b
c

不幸的是,你所能做的只是像 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 开发人员时远离的 class 语言的冗余地狱 :P

老实说,你应该只使用 classic 对象字面量,除非你真的需要正式的 class.

编辑:我想你可以在你的构造函数中接受一个对象文字,如果你想要文字的易用性 实际 class 的形式。

class Something {
  constructor(params) {
    Object.keys(params).forEach((name) => this[name] = params[name]);
  }
}

var something = new Something({
  param1: 'foo',
  param2: 'bar',
  param3: 'baz'
});

但是现在您刚刚将 class 变成了可以用任何属性实例化的动态 class,有点像对象字面量 :P

通常我想要一个class,因为我想形式化对象并呈现一致且严格可测试的API。

我们可以在每个 class 中创建一个静态方法,它接受 arguments 对象和一个名称数组,以及 returns 一个可以分配给新实例的对象 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 的主体是,我们也有这些优势:

  1. 您只写了一次参数名称。
  2. 您不会被缩小。

上面的代码包含额外的参数 (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
  }