web-dev-qa-db-ja.com

ES6クラスに関数を動的に追加する適切な方法

単一のメソッドexec(arg1,..,argn)を持つ単純なクラスがあり、事前定義された引数値を使用してexecを呼び出す多くのエイリアスメソッドが必要です(例:exec_sync = exec.bind(this, true))。

以下はトリックを行います:

_class Executor {
  constructor() {
    this.exec_sync = this.exec.bind(this, true);
  }

  exec(sync, cmd, args/* ... */) {
    // impl
  }
}
_

しかしこれが良いアイデアなのか、これがES6にとって慣用的なものなのかはわかりません。

DATE:

実際の例では、3つと4つのループを持つ2つのネストされたループがあり、合計12のエイリアスメソッドをクラスに動的に追加するために使用されます。プロトタイプベースのプログラミング言語であるJSを実際に利用できる場合、エイリアスメソッドを明示的に定義するのは面倒な作業です。

更新2-例:

メソッドrequest(method, body)を持つ単純なHTTPクライアントがあり、GETPUTなどのエイリアスメソッドを提供するとします。次のようになります。

_class HTTP {
  constructor() {
    ['GET', 'PUT', 'POST', 'DEL'].forEach((method) => {
      this[method] = this.request.bind(this, method);
    }, this);
  }

  request(method, body) {
    // execute the HTTP request
  }
}
_
25
Yan Foto

あなたの解決策は問題ありませんが、prototypeレベルで一度にこれらすべてのメソッドを作成する方が良いでしょう:

_['GET', 'PUT', 'POST', 'DEL'].forEach((method) => {
  Executor.prototype[method] = function (body) {
    return this.request(method, body)
  }
})
_

prototypeアプローチは、このコードが1回だけ実行されるため、少し高速です。一方、コンストラクターコードは、新しいインスタンスが作成されるたびに実行されます。

prototypeに対するconstructorのもう1つの利点は、クラスの継承と互換性があることです。したがって、後でクラスを拡張する場合、これらのメソッドのいずれかを再定義しても、何も壊れません。

ちなみに、ここにHTTP動詞のハードコーディングされた配列の代わりにrequire('http').METHODSまたは methods package を使用できます。

37

これが慣用的であることはわかりませんが(プログラミング言語自体ではなく、デザインに関するものであるため)、個人的には明示的な関数を作成する方が良いと思います。

exec_sync(...args) {
    return this.exec(true, ...args); 
}
1
zerkms

私は@leonidの答えが好きですが、動的に計算されたプロパティ名を使用するときにマングリングを許可する方法はありますか。

['VERYBIGNAME', 'VERYBIGNAME2', 'VERYBIGNAME3'].forEach((method) => {
   Executor.prototype[method] = function (body) {
     return this.request(method, body)
   }
})

あなたの縮小された、そして破壊されたJavaScriptにこれらのVERYBIGNAME文字列が存在します。

したがって、これらのVERYBIGNAME関数への参照を取得できる場合、次のようなものを使用できます

const references = { VERYBIGNAME1, VERYBIGNAME2, VERYBIGNAME3 };

Object.assign(Executor.prototype, { ...references });
1
BoltCoder