问题 ECMAScript 6中较短的类初始化


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

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

有没有办法让它更优雅,更短?我使用Babel,因此允许使用一些ES7实验功能。也许装饰者可以帮忙吗?


6458
2017-10-25 09:34


起源

它需要成为一个班级吗?您可以使用简写对象litteral将params指定为属性,而不必重复其名称。 - Touffy
为了回答你的上一个问题,装饰者在这里没有帮助,因为他们无法附加到 class constructor - CodingIntrigue


答案:


您可以使用 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...
  }
}

示例:(Babel的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

10
2017-10-25 09:36



这非常简洁,但你仍然在重复参数名称(一次在参数列表中,一次在分配调用中)。你可以克隆 arguments,删除预定义的属性,并将其传递给 assign。 - Touffy
@Touffy:是的 arguments 有 命名 参数的属性?我的意思是,除了以外的名字 0, 1,和 2?如果是这样,这对我来说是新的。 - T.J. Crowder
嗯,没错。我想你可以很容易地从函数的toString解析它们,但那变得很难看。 - Touffy
@Touffy:是的,这是一个有用的习惯用法,你可以继续使用,即使你只是需要一些映射到对象。 - T.J. Crowder
我要做的是将第一个参数视为属性映射,就像在Chev的最后一个解决方案中一样(当然,使用Object.assign而不是循环)。但是,如果您不相信调用代码发送正确的名称,那么您的解决方案会更安全。 - Touffy


不幸的是,你所能做的只是简单的事情 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。


3
2017-10-25 09:43



这很难看,但会给你函数的参数名称,这样你就可以在函数定义而不是外部数组中编写它们: arguments.callee.toString().match(/^function.*?\((.*?)\)/)[1].split(/,\s*/) } - Touffy
@Touffy那对我不起作用。严格模式不允许我访问 arguments.callee 所以你可能需要一个 进一步解决方法 对于那个解决方案,让它变得更加难看。 - CodingIntrigue


我们可以在每个类中创建一个静态方法 arguments object和一组名称,并返回一个可以使用分配给新实例的对象 Object.assign

用它来检查 巴贝尔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
  }

1
2017-10-25 09:56