10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaScriptでparseIntの仕様がいまいちなので、僕の作った最強のparseInt、を考えてみる。

Last updated at Posted at 2017-09-29

#parseIntの第二引数を書いているあなた。

書いていますよね。parseInt。
文字列から整数値に変換するときに使うやつです。

そして、そのときは必ず指定しますよね。第二引数。

これはねちょっとね。つらいですよ。

#毎朝

JavaScriptで文字列を数値に変換する:Number(), parseInt(), parseFloat() | UX MILK

毎朝ちゃんと飲むUX、のサイトで学んでおられると思いますので、毎朝ちゃんと parseInt について書いてますよね。正しく使いたいものです。文字列から整数変換にはparseInt。使いたいものです。

暗黙の型変換とか使っちゃだめですよ。
例えばですが、js parseIntで、検索すると上位に出て来るページ。

JavaScriptにおける数値⇔文字列の型変換あれこれ - console.lealog();

全体的な内容は非常にありがたいのですが、

'123'|0; // 123
ってすることが多いかなー。
やっぱりすっきり書けるのが良いですね。

うーん。これはちょっときつい。
JavaScriptの暗黙の型変換を利用して文字列数値変換をしているようだけど
これはぱっとみて、文字列を数値に変換していると理解するのが、かなり遠回りになります。

可読性が低いというか、JavaScriptの変な仕様を暗記しておかないと
文字列数値変換と理解することができない。というのはまずいコードです。
同様に、ゼロを引いたり、1をかけたりするのも、同じです。

parseInt使いましょう。
英単語を読んで字のごとく、というのは可読性がよいものになります。

#第二引数。

さて、肝心の第二引数です。

JavaScriptのNumber、parseIntの落とし穴(基数を与えないと想定外の数値に変換される) - Qiita

こえーよ。parseInt!
'020'の文字列を与えると16がかえってくる。
こんなのがバグになってしまうと発見が非常に難しいので、第二引数は省略せずに必ず指定した方がいい。

おっしゃるとおりです。

軽く動かしてみました。

  console.log(parseInt('020'));       //20
  console.log(parseInt('020', 10));   //20
  console.log(parseInt('020', 8));    //16

私の環境では、第二引数を指定しない環境でも20を出力してくれます。
古いブラウザだと、先頭0の入っている文字は8進数とみなして16になるらしいです。
環境に依存して返してくる値が違うって、きつい言語ですよ。相変わらず。

#オリジナルのparseIntを魔改造。

ということで、第二引数を10に固定しちゃえ!

  console.log(parseInt('020'));       //20
  console.log(parseInt('020', 10));   //20
  console.log(parseInt('020', 8));    //16

  var parseInt_original = parseInt_original || parseInt;
  var parseInt = function(str) {
    return parseInt_original(str, 10)
  };

  console.log(parseInt('020'));       //20
  console.log(parseInt('020', 10));   //20
  console.log(parseInt('020', 8));    //20

第二引数は無効化されました。

よかったよかった。これで安心....なわけはないです。
さすがに、標準関数を書き換えちゃったら、まずいです。

この魔改造コードがどこかに紛れ込んでしまい、
それをライブラリとして取り込んでしまったユーザーは
8進数表記や16進数表記を使おうとしてparseIntを呼び出しても動かない!誤動作する!
と、いきどおってしまうかもしれません。

#オレオレparseIntのススメ。

なので、自分でparseInt相当のものを標準とは別に作ってしまいましょう。

そもそも、parseIntは、第二引数を常に書かなければいけない。という問題もありますが、

文字列に数字以外のものが入っていたら、それを感知できずにその前までの数値を変換する。という、私からすると謎仕様があり

さらには文字列ではないものも引数として受け入れる。
みたいなことがあるので、このあたりもつぶしておきます。

変な引数の場合はnull値を返すようにしておきます。
文字列以外が渡されたときは明らかに間違っているので例外を発生しておきます。

index.html

<!DOCTYPE html>
<html lang="ja"><head>
  <meta charset="utf-8">
<script>

  console.log(parseInt('020'));         // 20
  console.log(parseInt('020', 10));     // 20
  console.log(parseInt('020', 8));      // 16
  console.log(parseInt(020, 10));       // 16
  console.log(parseInt(020, 8));        // 14
  console.log(parseInt('123', 10));     // 123
  console.log(parseInt('123a', 10));    // 123
  console.log(parseInt('123.4', 10));   // 123

  var oreore = {};
  oreore.assert = function(value, message) {
    if ((typeof message === 'undefined')
    || (message === null)) {
      message = '';
    }
    if (typeof value !== 'boolean') {
      throw new Error('Error:' + message);
    }
    if (!value) {
      throw new Error('Error:' + message);
    }
  };
  oreore.isString = function(value) {
    return (typeof value === 'string');
  };
  oreore.isNumber = function(value) {
    return ((typeof value === 'number') && (isFinite(value)));
  };
  oreore.isInt = function(value) {
    if (!oreore.isNumber(value)) {
      return false;
    }
    return Math.round(value) === value;
  };

  oreore.parseInt = function(str) {
    oreore.assert(oreore.isString(str));
    var result = Number(str);
    return oreore.isInt(result) ? result : null;
  };

  console.log(oreore.parseInt('020'));          // 20
  console.log(oreore.parseInt('020', 10));      // 20
  console.log(oreore.parseInt('020', 8));       // 20
  // console.log(oreore.parseInt(020, 10));     // エラーになる
  // console.log(oreore.parseInt(020, 8));      // エラーになる
  console.log(oreore.parseInt('123', 10));      // 123
  console.log(oreore.parseInt('123a', 10));     // null
  console.log(oreore.parseInt('123.4', 10));    // null

</script>
</head><body>
</body></html>

これで、第二引数はなく、不要な文字列が含まれたものは、nullを返す
oreore.parseInt ができました。

もちろん、oreoreオブジェクトとか、parseIntって名前自体も自由に変更可能です。

parseIntって名前の代わりに、strToInt とかでもいいかもしれないですね。

戻り値はnullの代わりに、undefinedやNaNを返したりとか、例外発生させるのもまた自由です。

改良して、あなたの使いやすいparseIntを実装してみてください。

#終わりに向けて
parseIntを作るためだけに、isInt isNumber isString assert なども追加していますが、これらもそれぞれ汎用的にいつでも使える関数になっています。

こうやっておけば、細かいことを暗記しておかなくても、関数名と実装の内部をちょっと見るとやり方を思い出しますし、ここから応用して様々な関数を作っていくことも、より容易になります。

自分のライブラリでは、isInt などの型確認と、isInts として複数引数を受け取る、型一括確認なども実装しています。

stsLib.js/stslib_core.js at master · standard-software/stsLib.js
ご参考にどうぞです。

#終わりに。
『人生を書き換える者すらいた。』と、いう人もいるくらいにプログラマとは現代の魔法使いか、錬金術師か、何かそういう存在として、いろいろな世の中の仕組みを書き換えているわけです。

人生を書き換えるほどの大層なことをしないまでも、自分の書くプログラミングスタイルを書き換えるために、元の標準関数をoreoreライブラリで書き換えるのもいいものですよ。

自分の書きたいようにプログラムを書くために、自分の書いているプログラミング言語の関数部品を書き換えていってみてください。

自分の使える魔法の呪文が増えていくようなもんですよ。

呪文を増やしてプログラミングを楽しんでいってください。

10
5
8

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?