Deprecated: preg_replace(): Passing null to parameter #2 ($replacement) of type array|string is deprecated in /home/stsoft/zero-plus-one.jp/public_html/wp-content/plugins/pz-linkcard/pz-linkcard.php on line 870
Deprecated: preg_replace(): Passing null to parameter #2 ($replacement) of type array|string is deprecated in /home/stsoft/zero-plus-one.jp/public_html/wp-content/plugins/pz-linkcard/pz-linkcard.php on line 898
Deprecated: preg_replace(): Passing null to parameter #2 ($replacement) of type array|string is deprecated in /home/stsoft/zero-plus-one.jp/public_html/wp-content/plugins/pz-linkcard/pz-linkcard.php on line 898
Deprecated: preg_replace(): Passing null to parameter #2 ($replacement) of type array|string is deprecated in /home/stsoft/zero-plus-one.jp/public_html/wp-content/plugins/pz-linkcard/pz-linkcard.php on line 870
Deprecated: preg_replace(): Passing null to parameter #2 ($replacement) of type array|string is deprecated in /home/stsoft/zero-plus-one.jp/public_html/wp-content/plugins/pz-linkcard/pz-linkcard.php on line 898
前回の記事で数値について扱いました。今回は、数値かどうかの型を判定します。
数値かどうかを判定する。なぜか昔から間違ったコードが正しいものとして紹介されている。
JavaScript で数値かどうかを判定するために、どういうコードを書くのが正しいんだっけ?と思って、グーグルで[JS isNumber]で検索してみました。
うひゃーーーーー!目も当てられません。グーグルで上位数件にでてくる、JavaScriptコードでのisNumber関数系の処理が、めちゃくちゃですごいです。JavaScript って罠が多いし、その言語の特性を認識してない人が多いから、こんなことになるのでしょうか。
嘘、を並べているわけではないのですが、実用的ではないテクニックを載せているサイトが多いです。JavaScriptを教える側の人ですら、しっかりと理解しているわけではないということですね。
そのように使っては駄目だ。というアンチパターンを学んでしまってそれが正解だと思いこむと、将来苦しくなるだけなのではないかな、と思ったりもします。理解していない、のではなく、間違った理解をしている、というのは問題がより複雑になってしまいます。JavaScript恐るべしです。
どのような間違いをしてはいけないという一応示しておきましょうか。
こちらの isNumber では、なぜか、引数に文字列が渡っているのに、数値として正しいと判定するisNumber関数を作っています。これは....。こんな関数は実用的ではないでしょう。文字なのに数値判定されたらわけわかりません。数値判定と数値に変換可能かどうかの判定とは、別関数にしなければいけないのに....
JavaScriptで値が数値かどうかチェックする:isNaN() | UX MILK
数値判定に isNaN 使うとかって、どうしたらいいのでしょうか....めちゃくちゃですね。サンプルコードの結果も無茶苦茶。「trueやfalseも数値として扱われます。」みたいな。このサイト、確か相当な有名サイトなはずなのですが......大丈夫でしょうか....というかだめでしょうね。
$.isNumeric() | jQuery 1.9 日本語リファレンス | js STUDIO
jQueryは極めて有名な、JavaScript黎明期によく使われたデファクトスタンダード的なライブラリです。このjQueryのisNumeric自体が、文字列を引数にとってもOKとしていた仕様だったようです。最初に作ってしまったから今更修正するわけにいかなかったのか。こんな実装。上級者は許容しないでしょう。もうjQueryの時代ではなくなってきているので、よかったです。
数字判定 | JavaScript逆引き | Webサイト制作支援 | ShanaBrian Website
ここも上位に上がっていた様子でしたが、他のサイトと同じ理由で、望ましい 数値判定とはいえません。
通常の数値かどうかはisNaN関数じゃなくてisFinite関数 - 三等兵
こちらのサイトはだめではないです。試行錯誤の結果がたくさんのっています。isFiniteについて紹介しているのは、とても助かりました。
数値かどうかを判定する、完全なコード
ということで、うちのサイトが一つの正解を示しますね。数値判定はこのようにやります。というか、実に簡単なことしかやっていないのです。なぜこの簡単なコードを、多くのサイトが間違うのか....JavaScriptの基礎中の基礎ができていないというかなんというか...。みなさん言語に関する教育的なWebページをやっているのに素人なのでしょうかね。....まあ、いいのですけれども。
基礎をちゃんと理解したい人は、このようなコードを書くのがよいでしょう。
1 2 3 4 5 6 |
const isNumber = function(value) { return ((typeof value === 'number') && (isFinite(value))); }; console.log(isNumber(123)); // true console.log(isNumber('123')); // false |
これで、引数が数値型なのかそうじゃないのかを判定することができます。「数値であり」そして「NaNやInfinityじゃない」ことを確認する関数です。
文字列を引数として渡したら、いくら数値を表していても、「数値ではないよ」という事を示してくれないと、実際のプログラミングの場面では困るわけです。
isFiniteでは NaN(数値型だけど数値じゃないを表す値→使わねーよこんな理不尽な定数) や Infinity(無限大を表す数値定数みたいなやつ→使わなーい!たぶん一生使わない!) は Falseになります。つまりこの isNumberではNanやInfinityは「数値ではない!」と判定するようにしています。
プログラミングの実用上、たとえば、計算結果に数値が入ってきてその結果で更に計算する場合、どこかで NaN や Infinity が入ってくると結果が正しく計算できない、ということになるので、「数値ではない!」と判断することが実用的だったりします。。isNumber は「この値は(計算することができる)数値なのか?」と判定するために使われるので、NaN や Infinity という、特殊、というか、変、な値は、数値の仲間には加えたくないのです。
完全なコードを、より完全にするためのテスト駆動開発コード
この isNumber が、ほぼ完全だと言えるためには、しっかりとした動作確認をするべきなんです。この関数がちゃんと動くかどうかのテストを書いて動きをたしかめるということになります。
これはテスト駆動開発といって、最近、主流になりつつある開発スタイルになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// 値が一致しているかどうかを確認する関数 // 一致していなければエラーが発生する const checkEqual = function(a, b) { if (a !== b) { throw new Error('Error:' + message); } } const isNumber = function(value) { return ((typeof value === 'number') && (isFinite(value))); }; checkEqual(true, isNumber(123)); // 数値は数値 checkEqual(true, isNumber(0)); checkEqual(true, isNumber(-1)); checkEqual(true, isNumber(123.4)); // 実数も数値 checkEqual(true, isNumber(123.0)); checkEqual(true, isNumber(10e+3)); // 特殊な記法でも数値 checkEqual(true, isNumber(10e-3)); checkEqual(true, isNumber(020)); // 8進数記法でも数値 checkEqual(true, isNumber(0xFF)); // 16進数記法でも数値 checkEqual(false, isNumber(true)); // Booleanは数値ではない checkEqual(false, isNumber(false)); checkEqual(false, isNumber(null)); // null は数値ではない checkEqual(false, isNumber(undefined)); // undefinde も数値ではない checkEqual(false, isNumber(Infinity)); // Infinity という特殊な数値型の値は Number ではないとしておく checkEqual(false, isNumber(NaN)); // NaN という特殊なという特殊な数値型の値は Number ではないとしておく checkEqual(false, isNumber('')); // 文字は数値ではない checkEqual(false, isNumber('ABC')); checkEqual(false, isNumber('ABC10')); checkEqual(false, isNumber('10ABC')); checkEqual(false, isNumber('0ABC')); checkEqual(false, isNumber('0')); // 文字列は数値に変換可能だとしても文字列なので数値ではない checkEqual(false, isNumber('5')); checkEqual(false, isNumber('-5')); checkEqual(false, isNumber('100')); checkEqual(false, isNumber('-100')); checkEqual(false, isNumber('10e+3')); checkEqual(false, isNumber('10e-3')); checkEqual(false, isNumber('020')); checkEqual(false, isNumber('0xFF')); checkEqual(false, isNumber([])); // 配列は数値ではない checkEqual(false, isNumber([1])); checkEqual(false, isNumber({})); // オブジェクトは数値ではない checkEqual(false, isNumber({ 1: 1})); |
これを実行するとなんにもなくだたそのまま終わります。そこが大事なんです。
checkEqual 関数によって、isNumber(xxx) の呼び出しの戻り値が true か false かを確定するように書いていてこのプログラムコードに問題があれば checkEqual 関数が例外を発生する(throwする)ようになっています。
このようなテストコードを書くということは多くの開発者がこの領域まで到達はしていない、スーパーテクニック級の開発スタイルといえます。統計をとれるわけはありませんが間違いなく5割以上、場合によれば7割か8割の開発者はこのコードがなんのかの意味がわからないはずです。
何も結果を出さないコードに何の意味があるの?とかなんとか理解できないかもしれません。
つまりこのコードの意味とこのようなコードを書いておくべきだという視点を身につけておくと、他の人達よりも一歩も二歩も十歩も百歩も抜きん出た、優れたプログラマになれるわけです。これを学べば平均以上のプログラマになる、という事になるかもしれません。
サンプルコードというのはこのように書くものです。これが、後々、プログラマとしてのあなたの腕をあげてくると思います。
テストコードとしても他サイトよりも丁寧にさまざまな場合を想定しているということがわかると思います。
追記:文字が数値に変換可能かどうかを判定する
そういえば、多くのサイトでは、isNumerが「数値かどうか」だけではなく「文字が数値に変換可能か」を見るためにもつかわれていて、わけわからなくて実用的ではない様子でした。
今回の記事で紹介した「isNumber」はそんな機能はなく文字列は数値に変換可能だろうとそうじゃなかろうと「数値じゃない!」と判定するように作っています。
では「文字が数値に変換可能か」を判断したい場合にはどうするのか、というと、私の場合は、そういう場合どうするかというと「ConvertToNumber 文字を数値(整数とか小数)に変換する」とか、「ConvertToInt 文字を整数値に変換する」関数を用意して、それを使って変換して、変換できなければ、null を返すものを作っています。
こちらから「ConvertToNumber」や「ConvertToInt」を検索してもらうと、ソースコードを読むことができるので、そちらを参照してみてください。
エンジニアとして、「数値かどうか」と「文字だけど数値に変換可能かどうか」を同じ関数の機能としてまとめるべきではない、というのは、通常の優秀なエンジニアだと、誰でも理解できることです。これを理解していない人が、プログラミングを教えていることで、ひどく雑なプログラムコードが世の中にあふれてしまうんだろうな、と思ってしまいます。
細かいことですが、プログラマなら、細かいことにこだわるからこそ、正確で不具合(バグ)の少ないよいものが作れるようになるんですよ。
JavaScriptの癖を見抜いて、よいソースコードを書くプログラマになっていってください。
追記:2019/05/09
続きの記事を書きました。ご参考ください。
追記 2019/05/10(金)
コメント欄で教えていただきました。
typeof value = 'number' なんて、現代的なJavaScriptでは使わないんですって!!!!
ES2015(もう4年前な気がするけどな)で定義されている Number.isFinite で置き換えできるそうです。
1 2 3 |
const isNumber = (value) => { return Number.isFinite(value); } |
これで十分です。(アロー関数式も使っておいた)
こちらで動作確認しました。よろしかったらどぞです。
とほほほ。知識がアップデートされていなかったです。
匿名さん、ありがとうです。