Hatena::ブログ(Diary)

三等兵

2010-09-16

valueOfとtoStringメソッドの水深43cmぐらいの深さの話

だいぶ前に、

var o = {
  i: 10,
  valueOf: function() { return this.i; }
}

のようなコード(もっと使い道のあるコード)を見たことがあった。そのときはvalueOfっていうとオブジェクトのメソッドて印象しかなくて、ほとんど使ったことがなかったからおっぺけぺーのおひゃーとスルーしましたが。

これはたとえば、

alert(o > 20); // false

と扱うことができる。なんでオブジェクトが数値と比較できてるんだと思ったけど、

alert(+o); // 10

数値として扱えた。

これはvalueOfメソッドによるもので、valueOfメソッドというのはオブジェクトを基本型などに変換する(数値型が多い)ときに呼び出され、オブジェクトを基本型として扱う場面では内部で自動で呼び出される。

ということで、この場合数値として振舞うべきだぜブランニューソーゥル?、という黒人的なノリでオブジェクトはvalueOfメソッドにより数値型になったのだ。(言い切った

上記の例はvalueOfをオーバーライドしてることになるのだろうけど、でも以前みたときはそういう思えなかった。リテラル表記だからかな。


それでtoStringもvalueOfと同じようなもの。

var o = {
  i: 10,
  toString: function() { return this.i; }
}

alert(o > 20); // false
alert(+o); // 10

そういうわけで同じような扱いができるvalueOfとtoStringの違いが分からない。今のところ同じだと言えるのは、オブジェクトを値として変換するときに自動で呼び出されるという部分。

たぶん他に何か違いがあると気になったので確かめる。


呼び出しの優先順位

valueOfとtoStringがあった場合、どちらを実行させるのかという話。あるとすればここかな。

var o = {
  i: 10,
  toString: function() {
    console.log('toString');
    return this.i;
  },
  valueOf: function() {
    console.log('valueOf');
    return this.i;
  }
}

alert(o);// 10と表示されtoString
alert(+o); // 10と表示されvalueOf
alert(''+o); // 10と表示されvalueOf
alert(String(o)); // 10と表示されtoString
alert(Number(o)); // 10と表示されvalueOf
alert(o == '10'); // trueと表示されvalueOf
alert(o === '10'); // falseと表示だけ

違いがあった。オブジェクトをどう変換するかでvalueOfとtoStringは使い分けているように思える。文字列ならtoString、数値ならvalueOfという具合。

alert(''+o);という微妙な場合はvalueOfにしている。プラス演算子があって文字列なのか数値なのかってところで、そういう場合はvalueOfが優先ということか。でもなんでだろ?

あと最後の同値演算子の場合はオブジェクトそのものを優先するので、オブジェクトを変換しないみたい。

そういうわけで求められる型が文字列ならtoStringで、数値ならvalueOfで、どちらかわからないのならvalueOf優先で呼び出す傾向があるようで。

論理値については数値か文字列で扱うことになるわけですから、同じように考えて大丈夫そう。


それぞれvalueOfとtoStringだけの場合

それぞれ優先して使い分けているということは分かった。なら片方しか存在しない場合はそれぞれどうなるのだろう。

まずtoString。

var o = {
  i: 10,
  toString: function() {
    console.log('toString');
    return this.i;
  }
}

alert(o);// 10と表示されtoString
alert(+o); // 10と表示されtoString
alert(''+o); // 10と表示されtoString
alert(String(o)); // 10と表示されtoString
alert(Number(o)); // 10と表示されtoString
alert(o == '10'); // trueと表示されtoString

valueOfがなくても結果は同じ。今度はValueOf。

var o = {
  i: 10,
  valueOf: function() {
    console.log('valueOf');
    return this.i;
  }
}

alert(o);// [object Object]と表示だけ
alert(+o); // 10と表示されvalueOf
alert(''+o); // 10と表示されvalueOf
alert(String(o)); // [object Object]と表示だけ
alert(Number(o)); // 10と表示されvalueOf
alert(o == '10'); // trueと表示されvalueOf

変化があるが・・・。この[object Object]はおそらくネイティブのtoStringによるものだと思うので、そこを少し変えてみる。

Object.prototype.toString = null;

var o = {
  i: 10,
  valueOf: function() {
    console.log('valueOf');
    return this.i;
  }
}

alert(o);// 10と表示されvalueOf
alert(+o); // 10と表示されvalueOf
alert(''+o); // 10と表示されvalueOf
alert(String(o)); // 10と表示されvalueOf
alert(Number(o)); // 10と表示されvalueOf
alert(o == '10'); // trueと表示されvalueOf

うん。うまいことできてるなしかし。

valueOfはオブジェクトを文字列として扱う場合、toStringメソッドを呼び出していることが分かった。そしてそのtoStringが利用できないときは、そのままvalueOfを実行するという流れ。

違いがどうとかいっていたけれど一部は同じみたい。

ああ、じゃあvalueOf優先というのは、オブジェクトを文字列として扱うかもしれないということを前提においての仕様なのか。もちろん全ての状況でvalueOfが優先なのかといえば分かりませんが。


そういうわけで違いについて

違いと言うか呼び出す基準があった。オブジェクトを文字列に変換するならtoStringで、数値ならvalueOfという傾向がある。(valueOfの場合正確には基本型に変換すると表現するのが正しい)

そして、toStringのみでオブジェクトを変換するときはvalueOfに一切触れずに行う。逆にvalueOfのみで変換するときは、文字列のときはtoStringを通じて変換する。ただし、toStringが使えないようならそのままvalueOfで返す。

こういうことができるのは演算のときに型を自動で変換するJSだからでしょうね。文字列だろうが数値だろうがだいたいこちらの望み通りの型にしてくれる。


とまあ、なんとも細かくて地味で微妙なものだった。

自動で実行する部分はどちらも一緒だけれど、valueOfはtoStringに通じているあたり気を抜いていると[object Object]などと出力されそうなので、気を抜いても扱いやすいtoStringを使うのが楽かも。


それぞれどう扱えば良いのか

そもそもデフォルトのvalueOfとtoStringは、

var o = {};
console.log(o.valueOf()); //  Object {}
console.log(o.toString()); // [object Object]

//ちなみにalertだとvalueOfも[object Object]になるけど、これはtoStringを呼び出しているようなのでやめた。

valueOfはthisを返し、toStringは型を返す。このようにデフォルトのままではあまり使い物にならない。というかvalueOfが活躍できそうもない。全部toStringに持っていかれやしないか。どっちもオーバーライドして使えるようにしなさいってサイ本にあったと思う。


あと、もしオブジェクトを基本型に変換して利用するなら、それぞれ文字列と数値で使い分けると良いかもしれない。

var os = {
  toString: function() { /* 文字列 */ }
}

var oi = {
  valueOf: function() { /* 数値 */ }
}

toStringだけで事たるわけですからどうでもいいっちゃどうでもいい。

あとここでいうvalueOfやtoStringはObjectオブジェクトでの話しであって、ArrayとかStringとかのオブジェクトのメソッドとは別。

midorimidori 2013/06/29 13:57 toStringは表示用、valueOfは評価用です

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/sandai/20100916/p1