LoginSignup
12
3

More than 3 years have passed since last update.

[JavaScript] template literals でなんちゃって string.format()

Last updated at Posted at 2019-07-06

やりたかったこと

Enum を定義しているような場合に、共通メッセージ用のテンプレート文字列を定義しておいて、必要な時に placeholder に対して任意の値を入れて使いたい。

例えば Python だと、string.format() を使うことで下記のようにできます。

message = "[Error] code: {code}, message: {message}"

print(message.format(code="000", message="error message text"))

# >>> [Error] code: 000, message: error message text

JavaScript ではこれに該当するメソッドがないので、独自の実装を作って使っている人も多いみたいです (ショシンシャー なのでよくわからん)。皆大好き stackoverflow では Yahoo (.com) のライブラリの例として下記のようなコードが貼られてました。

YAHOO.Tools.printf = function() { 
  var num = arguments.length; 
  var oStr = arguments[0];   
  for (var i = 1; i < num; i++) { 
    var pattern = "\\{" + (i-1) + "\\}"; 
    var re = new RegExp(pattern, "g"); 
    oStr = oStr.replace(re, arguments[i]); 
  } 
  return oStr; 
} 

// YAHOO.Tools.printf('{0} {1} {2}', 'aaa', 'bbb', 'ccc'); 

template literals 使う

もうちょっと手軽でスマートな感じにしたい、そこで思いつくのは
ES6 で入ってきたおなじみの template literals ですが、

const templateString = `[Error] code: ${code}, message: ${message}`;

上記の場合の問題点は、事前に code / message 変数が定義されている必要があることです。
そこで、文字列を下記のように関数で囲ってしまいます。


const templateString = ({code, message}) => `[Error] code: ${code}, message: ${message}`;

また上記の例では分割代入を使っているので、呼び出し側では引数の順番を考慮する必要がなく、キーに対して値を指定するのでコードの可読性も高いと思います。


const templateString = ({code, message}) => `[Error] code: ${code}, message: ${message}`;

// どちらも出力は同じ
console.log(templateString({code: '000', message: 'error message text'}));
console.log(templateString({message: 'error message text', code: '000'}));

// >>> [Error] code: 000, message: error message text

これなら楽ですね!

番外編

実は上記の方法を知るまでは、Tagged template literals を使って同様のことをやろうとしていました。仕様はドキュメントを見てもらえればと思いますが、下記のようになります。


// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates
// コードは上記からまんまなので、実際にはテンプレート文字列中で、
// `${0}` のように、整数 or `${'keyword'}` 形式で指定可能
function template(strings, ...keys) {
    return (function(...values) {
        var dict = values[values.length - 1] || {};
        var result = [strings[0]];
        keys.forEach(function(key, i) {
            var value = Number.isInteger(key) ? values[key] : dict[key];
            result.push(value, strings[i + 1]);
        });
        return result.join('');
    });
}

const templateString = template`[Error] code: ${'code'}, message: ${'message'}`;

console.log(templateString({code: '000', message: 'error message text'}));
// >>> [Error] code: 000, message: error message text

関数を定義しておき、テンプレート文字列を定義する時にこの関数を"タグ付け"しておくことで、呼び出し時に任意の処理を加えることができます。今回の例では単純に文字列の結合処理のみでしたが、Tagged template literals は他にも色々と応用が効きそうです。

参考: Tagged Template literals — Its more than you think

12
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
3