最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - A shorter class initialisation in ECMAScript 6 - Stack Overflow

programmeradmin2浏览0评论

Every time I create some class, I need to do the same boring procedure:

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

Is there any way to make it more elegant and shorter? I use Babel, so some ES7 experimental features are allowed. Maybe decorators can help?

Every time I create some class, I need to do the same boring procedure:

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

Is there any way to make it more elegant and shorter? I use Babel, so some ES7 experimental features are allowed. Maybe decorators can help?

Share Improve this question asked Oct 25, 2015 at 9:34 troorltroorl 1,6091 gold badge16 silver badges21 bronze badges 2
  • 2 Does it need to be a class? you could use the shorthand object litteral to assign the params as properties without repeating their names even once. – Touffy Commented Oct 25, 2015 at 9:38
  • 1 To answer your last question, decorators won't help here as they cannot be attached to a class constructor – CodingIntrigue Commented Oct 26, 2015 at 8:27
Add a ment  | 

3 Answers 3

Reset to default 10

You can use Object.assign:

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

It's an ES2015 (aka ES6) feature that assigns the own enumerable properties of one or more source objects to a target object.

Granted, you have to write the arg names twice, but at least it's a lot shorter, and if you establish this as your idiom, it handles it well when you have arguments you do want on the instance and others you don't, e.g.:

class Something {
  constructor(param1, param2, param3) {
    Object.assign(this, {param1, param3});
    // ...do something with param2, since we're not keeping it as a property...
  }
}

Example: (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);

Output:

a
b
c

Unfortunately, all you can do are simple things like Object.assign, but if you're trying to remove the redundancy of typing all the params twice (once in constructor signature and once in assignment) there isn't much you can do.

That said, you could do a hack like this. Though I'm not sure the effort and modicum of obfuscation that es with it is worth it.

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'

This way you're using a single array of argument names, then using that same array as a reference when creating the properties on your instance of Something. This pattern would work well in an Angular application where you're trying to preserve dependency names through minification by setting the $inject property.

Something.$inject = dependencies;

PS - Wele to the redundant hell of classical languages that I thought I got away from when I became a JS developer :P

Honestly, you should probably just use a classic object literal unless you really need the formality of an actual class.

Edit: I suppose you could accept an object literal in your constructor if you want the ease of a literal and the formality of an actual class.

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

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

But now you've just turned a class into a dynamic class that can be instantiated with any properties, kinda like an object literal :P

Usually I want a class because I want to formalize the object and present a consistent and strictly testable API.

We could create a static method within each class that takes the arguments object and an array of names and returns an object that can be assigned to the new instance using Object.assign.

Check it out using the 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')

Admittedly the addition of a method buildArgs means that this solution is not shorter, however the body of the constructor is and we also have these advantages:

  1. You are only writing the parameter names once.
  2. You are protected against minification.

The code above acmodates extra arguments (i >= paramNames.length) however we could modify it if this behaviour is undesirable such that these are still parsed, but not assigned to the instance:

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`
  }
}

Or ignored altogether:

  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
  }
发布评论

评论列表(0)

  1. 暂无评论