uhyohyo.net

JavaScript初級者から中級者になろう

十一章第六回 プリミティブについて2

前回に続いてプリミティブの話です。前回は文字列について主に解説しました。

今回はまず数値の話です。

数値の種類

数値といっても、数にはいろいろな種類があります。たとえば「整数」とか「有理数」、「実数」とか「複素数」です。

プログラミング言語によっては、「整数」しか扱えないint型や、小数も扱える(正確には無限に続く無理数は扱えませんが)float型やdouble型など区別されている場合もあります。また言語によっては虚数も扱えるものがあるそうです。

JavaScriptにおける数値では、一般に他の言語で「実数」と呼ばれているもの+αだと思えばよいでしょう。つまり、整数も小数も別け隔てなく扱えるということで、これをまとめて「数値」と読んでいるのです。

var a = 2;
a /= 3;
console.log(a);	//->0.6666666666666666

この例から、整数と小数を意識せずともうまく扱えることがわかるはずです。

ところで、「+α」というのは何かというと、JavaScriptの数値には、実数の範囲にとどまらない特別な値があるのです。1つはInfinityです。これはつまり「無限大」です。

数学では、一般には「0で割ってはいけない」ということになっています。しかし、JavaScriptにおいては0で割ってもエラーになりません。0で割った結果はInfinityになります。

console.log(3/0);	//->Infinity

Infinityとは「無限」ということです。つまり、JavaScriptにおいては0で割ると無限大になるものとして、無限大を数値の一種として扱っているということになります。

また、無限大にも正の無限大と負の無限大があります。負の値を0で割った場合には-Infinityとなります。

console.log(-5/0);	//->-Infinity

何かInfinityというと、急に英単語になったので違和感があるかもしれませんが、数値の一種とし扱われます。trueとかfalseとかnullとかundefinedのように特別な値ではないので注意しましょう。証拠に、typeofで調べてみましょう。

console.log(typeof (1/0));	//->"number"

Infinityの扱い

Infinityも数値ですから、四則演算その他の演算ができます。無限大に数値を足したり引いたりしても無限大のままです。

Infinity+100	//->Infinity
Infinity-50	//->Infinity

また掛け算割り算もInfinityのままですが、正の無限大に負の数をかけると負の無限大になります。そのへんは何となく理解できると思います。

Infinity*0.5	//->Infity
Infinity/(-2)	//->-Infinity

ちなみに、何気なくさっきから使っていますが、Infinityと直接書いても数値のInfinityになります。

しかし、これだけだと何かしっくりこないと思いませんか。例えば、無限大に無限大を足してももちろん無限大です。

Infinity+Infinity	//->Infinity

しかし、無限大から無限大を引くとどうなるでしょう。単純に0というわけにはいかず、数学的には、無限大から無限大を引いても無限大になる場合や、0になる場合などいろいろな場合があり決まりません。これを不定形と呼びます。

では、このように決まらない場合どのようになるのでしょうか。

console.log(Infinity-Infinity);	//->NaN

NaNという変なものが出てきました。これが次の話題です。

NaNの扱い

NaNとは、Not a Numberの略です。訳すと「数ではない」ということです。「非数」ともいいます。実は九章第七回で出てきていますが、深くは解説していませんでした。

しかし、実はこれが先ほどの「+α」の2つ目で、それも数値の仲間です。ですから、

typeof NaN

は"number"になります。

非数なのに数の仲間というのは変なものですが、実はNaNはいろいろ奥が深いのでそういうものだと思っておきましょう。

つまり、計算できないようなものは全部結果がNaNになります。例えば、無限大を無限大で割った形も不定形なのでNaNになります。

Infinity/Infinity	//->NaN

また、先ほど何かを0で割るとInfinityといいましたが、0で0を割った形は無限大になるとは限らず、これも不定形の一種であるので、

0/0	//->NaN

です。

また、NaNが出てくるのは計算時ばかりではなく、parseIntを使うときに出てきます。parseIntは、基礎第四回に何気なく出ていて、文字列を数値に変換するというものでした。

parseInt("123")	//->123

しかし、文字列を全て数値に変換できるわけではないのは明らかです。実は、そういう場合NaNが返ります。

parseInt("foooooo")	//->NaN
parseInt("")	//->NaN

ここでついでにparseIntについて詳しく解説しておくと、parseIntは、数値の後に文字列が続く場合はそれは無視して数値を返してくれます。

parseInt("123px")	//-> 123

また、parseIntは第二引数があり、それは基数です。つまり、その文字列を何進法として解釈するかということです。もちろん、デフォルトは10です。

例えば、10000は2進法では32を表しますから、

parseInt("10000")	//-> 10000(十進法)
parseInt("10000",2)	//-> 32(二進法)

基数は、2から36まで可能です。10より大きい基数では、16進法が有名でよく使われますね。

parseInt("ff",16)	//-> 255

16進法では、a〜fを10〜15番目の数字として扱います。同様に、gを16,hを17,...としてあてはめていくとzが35になりますから、36進法まで可能なわけです。

parseInt("z",36)	//-> 35

また、IntというのはInteger(整数)のことですから、parseIntは文字列を整数として解釈します。

parseInt("100.5")	//-> 100

これは、.の時点で数値が終了してあとの部分が無視されたと考えることもできます。

小数も解釈してほしいというときは、parseFloatを使います。これは小数でも正しく解釈してくれます。数値リテラル(後述)は全て解釈してくれると考えていいでしょう。

parseFloat("100.5")	//-> 100.5

ただし、parseFloatは小数も解釈できるようになった代わりに、10進数しか対応しません。そのため第二引数はありません。

さて、NaNの特徴は伝播することです。つまり、NaNが入った計算は全て結果がNaNになります。

NaN+3	//-> NaN
NaN*0	//-> NaN
Infinity+NaN	//-> NaN

これは、NaNが入ってしまったら結果をだしようもないので、当然といえます。

NaNの他の特徴は、真偽値に変換するとfalse(偽)になるということです。二章第十四回で、0やfalse、undefinedやnullなどはif文に渡すと偽になるといいましたが、実はNaNもそのうちの一つです。NaNの他には、0も真偽値に変換すると偽になりますね。ほかは真です。

数値の比較

つまるところ、JavaScriptの数値というのは、普通の小数(整数も含めて)と、±InfinityとNaNの3種類だということです。

上で数値の計算について紹介しましたが、数値の比較(等価演算子==とか不等号とか)はどうでしょうか。

Infinityについては直感通りの結果です。何と比較しても大きいです。

100<Intinify	//->true

-Infinityが何よりも小さいのも同様です。

ただし、NaNについては注意が必要です。NaNが入ると全てfalseになります。

NaN<3	//->false
NaN==3	//->false
NaN>3	//->false

そして、何と次のもfalseになります。

NaN===NaN	//->false

同じ物を等価演算子で比較してfalseになるのはNaNだけです。

isFinite

さてやはり、普通の数値と、InfinityやNaNは一線を画したところがあります。そこで、これらを区別する方法を解説します。ひとつずつ===等で比較してもいいのですが、便利な方法があります。

まずは関数isFiniteです。Finiteとは、Infinite(無限の)と逆で、有限ということです。ですから、これは数値の引数を一つとって、InfinityやNaNならfalseを返し、普通の数値ならtrueを返します。これで判別が可能です。

数値以外を渡したときは数値に変換(後述)してから判定します。

isNaN

もう一つ紹介するのがisNaNです。読んで字の如しという感じですが、引数に渡された数値がNaNならばtrue、そうでなければ(Infinityなども含めて)falseになります。isFiniteとは逆の関係なので注意しましょう。

isNaN(3)	//->false
isNaN(-Infinity)	//->false
isNaN(NaN)	//->true

数値リテラル

次はリテラルの話です。リテラルとは、JavaScriptのスクリプト上で何らかの値を指し示す文字列のことです。

例えば、JavaScriptで文字列を表すには、

"foobar"
'12345'

のように、""(ダブルクォーテーション)で描こうか''(シングルクォーテーション)で囲います。つまり、文字列を表現するにはこのように書かないといけません。

ですから、この""や''で囲まれたものを文字列リテラルといいます。他に、簡単なのが真偽値リテラルです。真偽値を表すには、

true
false

と書きました。この2つが真偽値リテラルです。他にnullもリテラルですが、実はundefinedはリテラルではなく変数ということになっています。スクリプト上にundefinedと書いた時はそれはundefinedのリテラルではなく、undefinedという値が入った変数が予め用意されているだけなのです。しかし、writable属性(十一章第四回参照)がfalseになっているので書き換えられません。

またリテラルはプリミティブばかりに用意してあるわけではなく、オブジェクトリテラル配列リテラルもあります。これらは既にお馴染みだと思います。

{
  foo:"bar",
  baz:3,
}
[0, 1, 2, 3]

のようなものがそれぞれオブジェクトリテラルや配列リテラルです。他に正規表現リテラルというのもありますね。

そして最後に残ったのが問題の数値リテラルです。実は数値リテラルというのはいろいろ種類が豊富なのです。それを紹介します。

最も一般的に使うのは、ただ数字を並べただけの次のようなリテラルだと思います。

0
123
15000

また、小数点を使ったのも数値リテラルの一種です。

0.5
3.00
3.14

ただし注意してほしいのは、次のようなのは数値リテラルではありません。

-3
-0.5

つまり、マイナスがついたのはひとまとまりの数値リテラルではなく、単項演算子の-(マイナス)の後に数値リテラルがくっついた値であるということです。その証拠に、

console.log(- 100);

のようにマイナスを離しても動作します。単項演算子-は、簡単にいうと数値の符号を逆にして返す演算子です。ですから、上のコードの場合、まず正の数100があって、-によって処理されて負の数-100(もちろん内部的には負の数もひとつの数として処理されています)が返されてconsole.logに渡されているのです。

また、この単項演算子-は、演算子ですから数値リテラル以外にも付けられます。

var num=10;
console.log(-num);	//-> -10

繰り返しになりますが、これはJavaScriptの数値は負の数を扱えないということではなく、あくまでプログラムを表記する際には負の数は単項演算子-を利用して書くという話ですので、注意しましょう。

さて、じつは、小数点だけ書いてその先を省略する形の数値リテラルも許されています。

3.	//3.0と同じ(つまり3と同じ)
0.	//0と同じ

これはなかなか見ない形だと思います。もちろんれっきとした数値リテラルですから、例えば次のように使えます。

console.log(3.*4.);	//-> 12

さらに、これにさらに指数部をつけることができます。指数部は、eのあとに数字を付けます。例えば、

1e4

という形です。1e4というのは、「1×104」を意味します。つまり指数というのは10の指数で、その数の桁をその数だけずらした数が得られます。

ですから、1e4というのは、1を104倍した数、つまり10000になります。

1e4	//-> 10000
2.5e3	//-> 2500
5.E2	//-> 500

最後の例のように、eは大文字でも構いません。

また、指数(eのあとの数字)は-(マイナス)を付けて負の数にしても構いません。例えば、

3e-2	//-> 0.03
1.3e-6	//->0.0000013

途中でマイナスが入っているので計算式のように見えますが、あくまでこれでひとつの数値リテラルです。数値リテラルですから、もちろん次のようにプログラム中で自由に使えます。

console.log(1e5-3.14e+2);	//-> 99686

また、eのあとに-がついていいなら+もついていいだろうということで、上で出たような3.14e+2のような形も許されます。意味は、+を省略した場合と変わりません。

さらに、16進数の記法が許されています。数値リテラルを0xまたは0Xで始めて、その後16進数の数字(0〜9,a〜fまたはA〜F)を並べることができます。この場合eを使って指数を示す記法は使えません。

0xff	//->255
0xDEADBEEF	//->3735928559

プログラム中では16進数を使うこともわりとあるので、その場合この記法を使えば見た目にも分かりやすくなります。

数値のメソッド

それではいよいよ、数値のメソッドについて見ていきます。

文字列の場合は、一時的にStringオブジェクトが作られてそのメソッドが呼ばれるのでした。数値の場合には、Numberオブジェクトが作られます。ですから、今から紹介するメソッドは全てNumber.prototypeにあるメソッドです。

toString

toStringというのは九章第七回で出てきましたね。その名の通り、数値を文字列に変換するメソッドです。

var num=10;
console.log(num.toString());	//->"10"

以前でてきたtoStringは引数をもたないメソッドでしたが、数値のtoStringは特別で、引数に基数を渡すとその進法で変換してくれます。parseIntと同じく、基数は2から36までです。

var num=10;
console.log(num.toString(2));	//->"1010"(2進法で10)

省略した場合はもちろん10進数です。

toLocaleString

また、Localeがついた関数がでました。Localeとは環境ということで、ユーザーの環境に合わせた結果が出るということです。

ただ、日本人(や英語圏の人々)は数字は普通に123とかそういう数字(アラビア数字)で書くのが普通ですから、toStringと同じような結果しか出ないでしょう。場合によっては、見る人に合った結果が出るかもしれません。

ただ、プログラム的というより、見やすさを重視した結果になります。例えば

var a=30000;
console.log(a.toLocaleString());

の結果は、2012年10月現在ですが、Google ChromeやOperaだと"30000", Firefoxは"30,000"という結果を返しました。

toLocaleStringは10進数しか対応していないので、引数はありません。

toFixed

どうしても、数値のメソッドは文字列に加工することくらいしかやることがないので、そういうメソッドが多くなります。toFixedもそうしたメソッドの一つです。

toFixedは、小数点以下の桁数を引数に指定して文字列にしてもらうことができます。小数点以下の桁数が足りない場合は0で埋めます。また、あふれる分は四捨五入して丸められます。

1.234.toFixed(3)	//-> 1.234
5.1.toFixed(5)	//-> 5.10000
123456.789.toFixed(2)	//-> 123456.79

小数点より上の位はそのまま表示されます。負の数に対しても正しく動作します。

また、桁数は0桁(整数に丸める)から20桁まで処理可能なことが保証されています。その範囲を逸脱する場合はエラーが発生する場合があります。

toExponential

Exponentというのは指数ということです。これは数字を、さっき数値リテラルで紹介したeを使った表記に直してくれます。引数はtoFixedと同じく、小数点以下の桁数です。

考え方は有効数字に近いもので、かならず小数点の前(1の位)に数字が一つくるようになります。

12345.6.toExponential(6)	//-> 1.234560e+4

これは、12345.6という数字は、有効数字7桁(1の位も含めて)でいうと1.234560×104であることを示した表記ですね。

また、桁数が足りない場合はtoFixedと同様四捨五入してくれます。

0.000045678.toExponential(3)	//->4.568e-5

toPrecision

最後に、さらに似たようなのをもう一つ紹介します。Precisionとは有効数字のことです。

これはtoExponentialとよく似たメソッドです。toExponentialでは小数点以下の桁数を引数として指定しましたが、toPrecisionでは小数点より前(1の位)の桁数も含めて考えますから、より一般的な有効数字に近い感じになるでしょう。

また、指数表示(e)を使わなくてもいけそうな範囲ではeを使わずに表示してくれるという違いもあります。toExponentialとの比較で結果を見てみましょう。

          3.0.toExponential(2)	//-> 3.00e+0
3.0.toPrecision(3)	//-> 3.00

0.05.toExponential(2)	//-> 5.00e-2
0.05.toPrecision(3)	//-> 0.0500

3e3.toExponential(1)	//-> 3.0e+3
3e3.toPrecision(2)	//-> 3.0e+3
3e3.toPrecision(4)	//-> 3000
3e3.toPrecision(5)	//-> 3000.0

0.0000001.toExponential(3)	//-> 1.000e-7
0.0000001.toPrecision(4)	//-> 1.000e-7

0.000001.toExponential(3)	//-> 1.000e-6
0.000001.toPrecision(4)	//-> 0.00000100

eを使った表示に切り替わる条件の一つは3番目の例からうかがい知ることができます。

条件は、その有効数字で少なくとも1の位まで全部表示できるなら数字を直接表示して、桁数が足りなくて1の位まで表示できない場合はeを使った表記になります。

また凄く小さい数については4番目に例を示しましたが、eのあとの指数が-7以下になりそうならeを使い、-6以上でおさまりそうなら直接表示します。

ある程度現れそうな数字の範囲が分かっていれば、eが出てこないので違和感ない表示もできるなど、わりと使い勝手のいいメソッドです。

以上で数値のもつメソッドの紹介は終わりです。

数値リテラルとプロパティ表記

上で、さっきから解説のために執拗に小数を使っていました。ピリオドが2つ並んで多少気持ち悪いかもしれませんが、1つ目のピリオドは小数点(数値リテラルの一部)で、2つ目のピリオドはリテラルとメソッド名をつなぐピリオドです。

3.0.toExponential(2)
~~~
            ここが数値リテラル

では、整数ではだめなのでしょうか。

3.toExponential(2)
~

実はこれはエラーになります。

なぜかというと、さっき上のほうで0.とか3.とか、「小数点以下を省略する記法」も、同じように数字にピリオドが続きますので、こちらが優先されるのです。

3.toExponential(2)
~~
ここがリテラル

するとこれは、

3toExponential

というのと同じ意味になり、これは文法的にはおかしいですね。ですからエラーになるのです。

解決策の一つは、()で囲んで分離することです。

(3).toExponential(2)	//-> 3.00e+0

もう一つの解決策は、次のような形です。

3..toExponential(2)

ピリオドが2つ並んだちょっと気持ち悪い形ですが、構造は次のようになっています。

3..toExponential(2)
~~
ここが数値リテラル

つまり、数値リテラルはピリオドが1つしか含みませんから、2つ目のピリオドが出てきた時点でもう数値リテラルは終了したと判断してもらうことができるのです。これも、数値リテラルの最後をピリオドで終わらせることができるからできる技です。

もちろん他にも、

3.0.toExponential(2)
3e0.toExponential(2)

などの形でもできますが、このピリオドを2つ並べる形が面白いのか、案外使われているようです。

ちなみに3e0のほうはまだピリオドが登場していませんが、eのあとには整数しかこないので、この形でも正しく判断してくれます。

Numberのプロパティ

Numberはコンストラクタであり、それ自体は関数です。しかし、Numberはそれ自身が、いくつかの情報をプロパティとして持っています。

Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY

これらのプロパティは、それぞれNaN,Infinity,-Infinityが入っています。前述のように、いちいちNumberを通さなくても取得できるのですが、より分かりやすさが欲しいとかいう場合はこちらが使われることもあるかもしれません。

Number.NaN	//-> NaN
Number.POSITIVE_INFINITY	//-> Infinity
Number.NETATIVE_INFINITY	//-> -Infinity

Number.MAX_VALUE, Number.MIN_VALUE

それぞれ「最大値」、「最小値」という意味です。本来実数ならばどこまでも大きくなることができ、最大値などはないのですが、コンピュータですから記憶できる量に上限があり、したがってJavaScriptの数値型で扱うことができる数にも限りがあります。もっとも、普段はこれが支障になることはないでしょう。

Number.MAX_VALUE	//-> 1.7976931348623157e+308

見てわかるようにMAX_VALUEはとても大きな数です。また、MIN_VALUEは負の数ではなく、正の数として扱える中で最も小さい数になります。つまり、なるべく0に近い数です。

Number.MIN_VALUE	//-> 5e-324

このあたりでは、演算が不安定になります。例えば、

var a= Number.MAX_VALUE;
var b=a-1;
console.log(a-b);	//-> 0
console.log(a==b);	//-> true

bのほうがaより1小さいはずなのに、aからbを引くと0になるし、a==bと言われてしまいます。10308という極めて多い世界では、たった1など小さすぎて記憶されないのです。

console.log(Number.MAX_VALUE+1 == Number.MAX_VALUE);	//-> true

同様に、1足したくらいでは変わらなくなります。これは、記録できる桁数に満たないため数が変わらないためです。それでは十分影響を与えるほど大きな数を足してみると、次のようなことが起こります。

console.log(Number.MAX_VALUE + 1.0e300);	//-> Infinity

限界を超えて大きくなった数はInfinityに変わります。ですから、

console.log(Number.MAX_VALUE + 1.0e300 -1.0e300);	//-> Infinity

このように同じ数を足して引いても結果が変わるということも起こります。また、最初から大きすぎる数を表現しようとした場合もInfinityになります。

console.log(1e400);	//-> Infinity

しかし、実際の使用の範囲ではこのような問題はめったに発生しないでしょう。

ちなみに、MIN_VALUEについても、JavaScriptで表現できる数の中で0より大きい中で最も小さい数なので、その間の数は出現しません。 Number.MIN_VALUE-2.47032822e-324 //->5e-324 Number.MIN_VALUE-2.47032823e-324 //->0

真偽値

さて、数値についても終了したので最後に残った真偽値のプリミティブの話に移りたいところですが、真偽値については特筆事項はありません。Boolean.prototypeには特にメソッドなどはないということです。

プリミティブの変換

JavaScriptには、プリミティブの変換処理が搭載されています。この話題はある程度、九章第七回で触れています。

例えば減法演算子とかで値をプリミティブ(この場合は数値)に変換する必要がある場合、例えば文字列とか真偽値とかが渡されたら困るので、それも処理できるように変換規則が用意されているのです。

まず、オブジェクトを何らかのプリミティブに変換したい場合は、メソッドtoStringまたはvalueOfを呼び出して何らかのプリミティブに変換してから、さらにプリミティブ同士の変換を適用するのでした。ちなみに、文字列に変換したいときはtoString,数値に変換したいときはvalueOfです。また、オブジェクトを真偽値に変換したいときは、プリミティブに変換する処理をとばしてtrue扱いになります。

ですから、ここではプリミティブ同士の変換を見ていきます。

文字列への変換

文字列への変換は単純です。undefinedやnull,真偽値のtrueやfalseはそのまま"undefined","null","true","false"になります。数値の場合も数値を表す文字列になります。たとえば3なら"3"という文字列になりますね。

数値への変換

数値への変換になると多少変な点が出てきます。

まず文字列を数値に変換する場合は、parseIntのような処理がされますが、違いがあります。parseIntは"123px"のようにうしろに文字列がくっついている場合それを無視しましたが、この場合の変換では後ろに文字列がくっついている場合もNaNになります。減法演算子は両辺をまず数値に変換しますから、「0を引く」という変換方法で試してみましょう。

console.log(parseInt("123px"));	//-> 123
console.log("123px"-0);	//-> NaN

また面白いのが、真偽値の変換です。以前も紹介しましたが、trueは1でfalseは0になります。何気なく計算に真偽値が紛れ込んでもNaNが発生せずに通ってしまうので怖いですね。困る場合はtypeofで確認するなどして対処しましょう。

true+true	//-> 2

またさらに、nullも0に変換されます。

null+2	//-> 2

一方、undefinedはNaNに変換されます。

undefined-3	//-> NaN (NaN-3が計算された)

真偽値への変換

真偽値への変換は、二章第十四回でも触れたように、if文に渡す場合などに行われるので意識する必要があります。これはよく使います。

数値の場合は、0とNaNはfalse、他はtrueに変換されます。

文字列は、""のみfalse、他はtrueです。"0"もtrueになるので注意しましょう。

null,undefinedはfalseです。

整数への変換

JavaScriptにはビット演算子というのがあります。ビット演算は七章第二回で出てきています。

JavaScriptでは数値においては整数も小数も区別されませんが、ビット演算は整数にしか適用できません。そこで、この場合、渡された数値を整数に変換してから処理されます。変換方法は小数点以下切り捨てです。

8|3.6	//-> 11 (8|3になった)

この|(論理和)は、n|0に対してnを返す性質がありますから、数値を簡単に整数に変換したい場合にこの方法が使われることがあるので注意しましょう。

1.234|0	//-> 1

小数の整数への変換には、もっとちゃんとした関数(Math.floorなど)を使う方法もありますが、これは後々紹介します。

これでプリミティブの扱い方に関する話は終了です。