obj+""≠obj.toString()≠String(obj)っていう話

#前略
 
本記事でのJavaScriptは基本的にECMAScript 5のこととし、地の文に出てくる変数 obj はオブジェクトであるとします。
 
#導入
 
こんな感じのJavaScriptのコードを時々見かけます。
 
```javascript
 // obj.toString()
 str = obj+"";
```
 
`obj`を文字列に変換する、というコードですね。
この方法は同じように文字列に変換する`obj.toString()`や`String(obj)`に比べてタイプ数が若干短いなどのメリット、メソッドの参照や関数呼び出しが絡まないため速いという都市伝説（都市伝説です。`String(obj)`の方が速いこともあります[要出典]）があります。しかし、果たしてその中身は本当に同じものなのでしょうか？
 
#実験
 
`obj+""`が`obj.toString`や`String(obj)`と同じであるか確かめるため、次のようなコードを実行してみます。
 
```javascript:testTransString1.js
var obj = {
    toString : function(){
        return "toString";
    },
    valueOf : function(){
        return "valueOf";
     },
};
 
console.log(obj.toString()); //=> "toString"
console.log(obj+""); //=> ???
console.log(String(obj)); // ???
```
 
さて、コンソールにはどのように表示されたでしょうか？
まともなJavaScript実装であれば、下のように表示されるはずです。
 
    "toString"
    "valueOf"
    "toString"
 
上から`obj.toString()`、`obj+""`、`String(obj)`です。
 
注目すべきは二番目。
`"toString"`ではなく`"valueOf"`が返されている、つまり`obj.toString()`ではなく`obj.valueOf()`が__暗黙的に__呼ばれたということになります。
 
Javaをやっている人は首を傾げるかもしれません。JavaScriptはJavaのように文字列の連結時にオブジェクトがあったとき、暗黙的に`toString`が呼ばれるわけではないのです。
 
では`toString`はどのようなときに呼ばれるのでしょうか？
次のコードを実行してみてください。興味深い結果が現れるはずです。
 
```javascript:testTransString2.js
 var
 obj1 = {
     toString : function(){
         return "obj1 toString";
     },
     valueOf : function(){
         return "obj1 valueOf";
     },
},
obj2 = {
    toString : function(){
       return "obj2 toString";
    },
    valueOf : function(){
        return {};
    },
},
obj3 = {
    toString : function(){
        return {};
    },
    valueOf : function(){
        return {};
    },
};
 
[obj1,obj2,obj3].forEach(function(obj){
    console.log(obj+"");
});
```
 
`obj1`はさっきの`obj`の返す文字列を変えただけ、そして`obj2`は`valueOf`で`obj3`は両方のメソッドで`{}`（空のオブジェクト）を返しています。
 
結果は以下のようになりました。
 
    "obj1 valueOf"
    "obj2 toString"
    TypeError : .... 
 
最後は例外が発生しています。
この違いにはどのような意味があるのでしょうか？
色々考えてみてください。
 
#解説
 
JavaScriptの二項の+演算子には、数値の加算・文字列の連結という二つの役割があります。
この二項の+演算子は、次のようなプロセスで評価されます（もちろん実際の仕様はこんなに単純じゃありません。が、ここで仕様に忠実に説明しても仕方ないので簡単に説明しています。興味ある人は [ECMA-262](ECMA-262) の11.6 Additive Operatorsの項を参照）。
 
1. 左辺から右辺へ式を評価する。
2. _それぞれの結果_ をプリミティブ値（Number、String、Boolean、Null、Undefined型）に変換する。
3.  
    1. どちらかが文字列であれば、 _それぞれのプリミティブ値_ を文字列に変換して連結する。
    2. どちらも文字列でなければ、 _それぞれのプリミティブ値_ を数値に変換して加算する。

また、プリミティブ値に変換するプロセスは次のようになっています（さきに同じ。詳しくは[ECMA-262](ECMA-262) の9.1 ToPrimitiveを参照）。

1. 元からプリミティブ値ならそれを返す。
2. オブジェクトなら、
    1. `valueOf`プロパティーが関数か確認し、関数であれば実行する。関数でなければ、3に飛ぶ。
    2. _返り値_がプリミティブ値ならそれを返す。
    3. `toString`も同様にして、_返り値_がプリミティブ値ならそれを返す。
    4. プリミティブ値を返せなかったなら`TypeError`。

この仕様を踏まえて、さっきのコードを見てみましょう。
`obj1`はまず`valueOf`が呼び出され、`"obj1 valueOf"`が返ってきて、これはプリミティブ値なのでそのまま返され`""`と連結します。
`obj2`も`valueOf`が呼び出されますが返り値は`{}`でプリミティブ値ではないので、`toString`が呼び出され、今度は返り値が`"obj2 toString"`とプリミティブ値なので返され連結されます。
`obj3`では`valueOf`も`toString`も呼び出されますが、どちらも返り値がプリミティブ値ではないので`TypeError`が投げられる、というわけです。

ですが、この仕様が実際の開発中に問題になることは少ないかと思います。
なぜなら、全てのオブジェクトのデフォルトである`Object.prototype.valueOf`が

```javascript
Object.prototype.valueOf = function(){
    return this;
};
```

のように実装されているため、`valueOf`がプリミティブ値を返さないため、`toString`を自分で実装したときに意図したように呼ばれるからです。
しかし、何らかの理由で`valueOf`を実装した場合、`obj+""`では不具合が生じることがあるでしょう。

ちなみに関数（コンストラクタではない）`String`の場合は、これとは反対に`toString`を呼んでプリミティブ値が返されなければ`valueOf`を呼び、その値を文字列に変換する、というプロセスとなっています。

```javascript
var
obj1 = {
    toString : function(){
        return {};
    },
    valueOf : function(){
        rettrn "valueOf";
    },
},
obj2 = {
    toString : function(){
        return {};
    },
    valueOf : function(){
        return {};
    },
};
 
console.log(String(obj1)); //=> "valueOf"
console.log(String(obj2)); //TypeError!
```
 
あと`obj.toString()`ですが、これは単に`obj`の`toString`メソッドを呼んでいるだけなので、文字列以外が返ってくる可能性も否定できません。（もちろん、文字列を返すべきですが…

#まとめ

- `toString`は__Javaのように__文字列の連結に暗黙的に使用されるメソッドではない。
- `valueOf` も__Javaのように__文字列を数値オブジェクトに変換するメソッドではない。
- `obj+""`はまず`obj.valueOf()`を呼び、`obj.toString()`を呼ばないことがある（がしかし、実は一つだけ例外がある）。
- `String(obj)`は間違いなく `obj.toString()` を呼ぶ。
- つまり`obj+""`は`String(obj)`、`obj.toString()`の完全な代替とは呼べない。

`obj+""`は実際の開発でもよく使われるテクニックですが、以上のような問題点も孕んでいることを知っておいて損はないのでしょうか？
付け加えて、必ず文字列を得たい面なら

```javascript
 str = String(obj.toString());
```

が最も期待する動作に近いのではないかと思います。（もちろん状況にもよりますけどね^^;

#感想

JavaScriptが__中途半端にJavaっぽくした__のが裏目に出た、良い例になったような気がします。
あとは、この記事を書くために [ECMA-262](ECMA-262) に手を出したので、それはそれでいい経験になったかな、と。

はぁ…。
なんて言うか、自由研究みたいな構成だなぁ…。

荒削りの知識ですので、間違い等ありましたらコメントしていただけるとありがたいです。

[ECMA-262]:http://www.ecma-international.org/publications/standards/Ecma-262.htm