3165


ECMAScriptにおけるtagged templateを動的に生成する方法について教えて下さい。 template
stringsは、一般的には、template
literalsという呼称のとおり以下のコード例のようにリテラルとしてソースコード内に固定値として保持されるものですが、

[code]

    var name = "user input here";
    var str = `Hello, ${name}!`;
    
[/code]

変数に格納された文字列をテンプレート文字列としてtagged template stringsとして利用したいと考えています。
この場合、evalやFunctionコンストラクタを使って文字列からコードを生成する以外にスマートな方法はないのでしょうか。

[code]

    var name = "user input here";
    var template = "Hello, ${name}";
    var tagfunc = function(){ ... };
    var str = eval( "tagfunc`" + template + "`" );
    
[/code]

変数`template`の内容が多言語化のために設定ファイルなどで指定されているといった状況を想定して下さい。
この場合、`template`の内容そのものは開発者の支配下にあり実質的にevalされるコードは固定的な内容と等しいため、XSS等のセキュリティ上の心配はほぼないのですが、それでもContent-
Security-Policyの導入などを考えると極力コード中のevalの使用は控えたいと思っています。


CC BY-SA 3.0
2015-01-06T01:15:08.310
3153
2015-01-06T06:23:09.240
2015-01-06T01:46:03.753
30
2353
question
javascript ecmascript-6
変数からtemplate stringsを生成したい


無理だと思います。[テンプレートリテラルは文字列リテラルとは違います](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-
template-
literals)。またテンプレートリテラル内の式表現は解決後の値でしかスクリプトから参照できないようです。テンプレートを動的に生成し評価させたいのなら、ソースコードを再評価させることになりますから、
`eval` を使用する方向になるでしょう。

同じようなテンプレートエンジンを自前で作るにしても、名前空間の仕組み上、呼び出した関数の中では呼び出し元の変数を参照できないので、変数名を指定するようなテンプレートの作成は難しいところです。特に宣言済みのローカル変数名リストへのアクセスができないのが痛い。

それでも、シンプルな値置換のテンプレートエンジンは簡単に作成できる言語ですので、値を変数名のみではなく、`eval` 前提表現、または、DSL
で表現できるようにしてゆけば、同じようなことができるようになるかもしれません。

[code]

    // 辞書情報をもとにする、シンプルな置き換えテンプレートエンジン
    function apply_tpl(tpl, dict){
      Object.keys(dict).forEach(function(k) {tpl = tpl.replace(new RegExp('\\${' + k + '}','g'), dict[k]);})
      return tpl;
    }
    
    // 使い方: 辞書
    apply_tpl("Hello, ${name}", { name: "Arthur"} )
    // => Hello, Arthur.
    
    // 使い方: オブジェクトに定義されているプロパティ (プライベート変数以外)
    var value1 = "Cathy"
    
    apply_tpl("Hello, ${value1}", this)
    // => "Hello, Cathy"
    
    apply_tpl("Hello, ${value2}", this)
    // => "Hello, ${value2}"
    
[/code]


CC BY-SA 3.0
2015-01-06T05:11:54.003
3165
2015-01-06T05:11:54.003
4978
3153
answer


設定ファイルをJSで記述し、一番テンプレートリテラルそのままで使える方法を考えてみました。

～～定義～～

[code]

    function MyTemp(scope_obj) {
      return key => {
        with(scope_obj) {
          return {
            HELLO : `Hello, ${name}!`,
            HELLO2: `Hello2, ${name}!`,
            HELLO3: `Hello3, ${name}!`
          }[key]
        }
      }
    }
    
[/code]

～～利用～～

[code]

    var scope_obj = {
      name: "Mike"
    }
    var temp = new MyTemp(scope_obj)
    var str = temp("HELLO")  // "Hello, Mike!"
    
[/code]

さらにこれにProxyなどを活用すれば下記のような感じなど、気に入った表現方法で書けるようにもできるはずです。

～～利用～～

[code]

    with(MyTemp){      // scope_proxy
      ......
      $name = "Mike"   // $小文字で始まる変数への代入はproxyが受け入れテンプレートで使えるようにする
      var str = $HELLO // "Hello, Mike!"
                       // $大文字で始まる変数への参照でテンプレートを解決して返す
      ......
    }
    
[/code]


CC BY-SA 3.0
2015-01-06T06:23:09.240
3169
2015-01-06T06:23:09.240
5721
3153
answer