JavaScriptで値を文字列化するにはいくつかの方法が考えられる。
toString メソッド
最初に思いつくのは toString
メソッドだろう。「文字列に変換したい!」という気持ちがソースコードからも読み取りやすい。
x.toString()
しかし、 toString
メソッドの実装次第では、 .toString()
で文字列以外の値が帰ってくる可能性がある。例えば、 x = {toString(){return 123;}}
とすると x.toString()
の型は number となる。
String 関数
String オブジェクトを関数呼び出しすると、値が必ず文字列に変換されて帰ってくる。
String(x)
(new String(x)
ではない)
グローバル変数 String の指す値が組み込みの String オブジェクトならこの性質は成り立つが、グローバル変数 String が変更されていたらその限りではない。
String = function(x) { return x; }
String(123) // => 123
空文字列との連結
""+x
文字列と値を +
で連結すると文字列が得られる。特に空文字列を使えば、値の文字列化に使える。
テンプレート文字列
`${x}`
ECMAScript 6 で追加されたテンプレート文字列を使えば、値を必ず文字列に変換できる。
これらの方法の違い
ここまでに挙げた方法のうち、「空文字列との連結」と「テンプレート文字列」の2つは、結果が文字列になることが(実行時の環境によらず)ソースコードの静的解析で判断できる。さて、この2つは等価だろうか?
実はこの2つは等価ではない。つまり、 ""+x
と `${x}`
の結果が等しくないようなオブジェクト x
を作ることができる。具体的には次のようにすれば良い:
let x = {
toString() { return "hoge"; },
valueOf() { return "piyo"; }
};
console.log("" + x); // => "piyo"
console.log(`${x}`); // => "hoge"
""+x
と `${x}`
では、 x を文字列に変換する過程が異なる。ざっくり言うと +
演算子は数の足し算にも使える関係で、プリミティブ値への変換の際に valueOf
の方が優先される。一方、`${x}`
は結果を文字列に変換することがわかっているので toString
の方が優先される。
詳しくは ECMAScript の仕様書を読まれたい。
ちなみに、 String 関数の挙動は `${x}`
と同じである。
トランスパイラの挙動
テンプレート文字列は ECAMScript 6 で導入された比較的新しい機能なので、ES5以前にトランスパイルされることもまだまだ多いかと思う。 `${x}`
を安直に "" + x
に変換してしまうと挙動が変わってしまう可能性があるというのはすでに見た通りだが、世間で使われているトランスパイラはどうしているのか?
というわけで試したところ、
- Babel 6.25.0:
"" + x
- TypeScript 2.4.1:
"" + x
という結果だった。`${x}`
と "" + x
の挙動の違いのような重箱の隅は無視しても問題ないということなのだろうか。