UUUM攻殻機動隊(エンジニアブログ)

UUUMのエンジニアによる技術ブログです

タグ付きテンプレートリテラル <JavaScript の「なんだこれ・・・」その1>

前置き

序文

  • JavaScript の「なんだこれ・・・」シリーズの第一弾は、「タグ付きテンプレートリテラル (Tagged Template Literal)」です。

仕様

  • ES6 で追加された仕様に、テンプレートリテラル (Template Literal) があります。他の言語ではよく見られる機能です。この機能を使えば、変数の値を文字列中に埋め込みたい時に文字列連結演算することなくスマートにコード化できます。

  • 色々な言語のテンプレートリテラルを見てみましょう。自分が業務で扱ったことのある言語の例を挙げてみました。

# Ruby

love = '最愛の'
"#{love}嫁さん (^-^)"    # '最愛の嫁さん (^-^)'
// PHP

$love = '最愛の';
"{$love}嫁さん (^-^)"    // '最愛の嫁さん (^-^)'
# Python

love = "最愛の"
f"{love}嫁さん (^-^)"    # "最愛の嫁さん (^-^)"
// Kotlin

val love = "最愛の"
"${love}嫁さん (^-^)"    // "最愛の嫁さん (^-^)"
// Swift

let love = "最愛の"
"\(love)嫁さん (^-^)"    // "最愛の嫁さん (^-^)"
// Java

// テンプレートリテラルは存在しない   // "それでも最愛の嫁さん (^-^)"
  • Java にはテンプレートリテラルはありませんが、嫁さんへの愛は変わりませんでした。

  • 多言語を扱っていると、別プロジェクトの作業に移った際にちょっとした書式の違いにいつも悩まされます。コンパイラの構文解釈的な都合なのかなと考えてますが、一度その意図をコンパイラの作者に訪ねてみたいです。

JavaScript のテンプレートリテラル

  • それでは、ES6 で追加された JavaScript 版をみてみましょう。
// JavaScript

const love = "最愛の";
"${love}嫁さん (^-^)"    // "最愛の嫁さん (^-^)"
  • 方向性は Kotlin と同じようですね。嫁さんへの愛も同じです。

タグ付きテンプレートリテラル

  • 何かで調べ物をしている時に、「タグ付きテンプレートリテラル (Tagged Template Literal)」と出会いました。初めてコードを見た時、「( ) が抜けてるんじゃないの?」と、しばらくフリーズしたのを覚えてます。
// タグ関数を定義
function scaleTag(hashes, ...values) {
  return values
    .map((value, index) => hashes[index] + emphasize(value))
    .concat(hashes.slice(values.length))
    .join("")
  ;

  function emphasize(word) {
    if (!word) {
      return "";
    }

    return `<strong>${word}</strong>`;
  }
}

const love = "最愛の";

/*
 * 以下が「タグ付きテンプレートリテラル」の書式です。
 */
scaleTag `${love}嫁さん (^-^)`;
    // "<strong>最愛の</strong>嫁さん (^-^)"
  • 上のコード中で「scaleTag」がタグと呼ばれる箇所です。この書式を初めて見た時、まるで「ずっと昔の恋人の経年後の今を SNS で偶然見かけたような気分」で見つめていました。

  • タグとテンプレートリテラルの間に半角スペースを入れていますが、なくても動作するようです。

解説

  • テンプレートリテラルにタグを付与すると、変数を展開する際にその値を加工するための hook 処理ができるという仕様。使い所は難しいですが、以下の用途はいかがでしょう。
// 整数をカンマ区切りで表示するタグ
function commalizeTag(hashes, ...values) {
  return values
    .map((value, index) => hashes[index] + commalize(value))
    .concat(hashes.slice(values.length))
    .join("")
  ;

  function commalize(number) {
    return number
      .toString()
      .replace(/(?!^)(?=(?:\d{3})+$)/g, ",")
    ;
  }
}

const price = 2500;

commalizeTag `価格:${price}円`;    // "価格:2,500円"
  • 「price を commalize() で変換してから表示させる」という方法だと、文字列中に commalize(price) と記載することになりますが、その辺りのノイズが外出しされるので、文字列がどういう結果になるかを想定しやすくなるメリットはありそうです。
// どちらが良いか?
commalizeTag `価格:${price}円`;
`価格:${commalize(price)}円`;
  • 変数展開がひとつだけなら大差ないですが、例えば Excel のように金額がずらりと並ぶような表を Web ページで表示させる場合はどうでしょう。commalize() が価格表示の個数分だけ書くのに比べれば、テンプレートの直前に一回だけ記載する方法はスマートに感じます。

  • 他にもっと良さそうなサンプルを閃いた時は、随時追加していきます。

まとめ

  • JavaScript のコードに違和感を覚えてそこで立ち止まるのは、バグを探す目が自動的に反応しているのだと考えてます。超初心者の頃は、構文を知らずによく立ち止まっては「ああ、こんな構文があるのか!」と、感動して覚えたものでした。しかしながら、JavaScript で立ち止まってそれが新しい構文だったという感覚は、久しく体験していませんでした。

  • 街でふと昔の恋人を見かけて。見たことのなかった笑顔で歩いている様子に「ああ、今は幸せなんだね」と感じたような、そんな「なんだこれ・・・」でした。

  • UUUM では、技術にこだわりのあるエンジニアを募集しています!! 詳しくは以下のリンクをご覧下さい。

www.wantedly.com

www.wantedly.com