銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

JavaScriptのヤヤコシイとこ

f:id:takamints:20201031232344j:plain

会社ではJavaScriptおじさん的になっています。主にNode.jsでバックエンドですが、軽めのフロントエンドもたまにやります。

そんなJavaScriptで、このおじさん。最近ちょくちょくやらかしています。 若い人に間違いを指摘されたりしてお恥ずかしい。 少なからず迷惑もかけております。

そんなこんなで、誠心誠意、懺悔と言い訳&最終的にはJavaScriptへの愚痴になるけど、「JavaScriptのこういうところがヤヤコシイですー」ってところを書いておきます。 JavaScriptは大好きですけどヤヤコシイのだ。

目次


空文字列は0である?

先日、以下のようなコードを書いて、思った通りに動いて無くて困りました。

const a = "";
if(a == 0) {
    console.log("値はゼロ");
} else {
    console.log("値は空文字列");
}

変数 a を 空文字列で初期化して「aは0である」という条件判定をしています。 これ、直感的には「偽」ですが、 JavaScriptでは「真」になります。 つまり上のプログラムを実行すると「値はゼロ」と表示されます。

これを正しく判定するには=を3つ書いて a === 0 としなくてはなりません。

const a = 0;
if(a === 0) {
    console.log("値はゼロ");
} else {
    console.log("値は空文字列");
}

やってみましょう。以下は Node.js のREPLでの実行結果です。 コンソール(Bashコマンドプロンプト、パワーシェルなど)を開いて node と打てば対話型のインタプリタ(REPL)が起動します。 行頭が > となっている行の右の文字列を入力して[Enter]で一行ずつ実行できます。

$ node
Welcome to Node.js v12.18.2.
Type ".help" for more information.
> let a = 0
undefined
> a
0
> a == ""
true
> a === ""
false
>

これはStrict モードでも同じです。REPLをStrictモードで開くには node --user_strict で起動します。

型に寛容なJavaScriptの世界ではかなり有名で、自分もしっかり意識しているつもりでした。 が、ついうっかり「==」と書いてしまったんですね。 こういった「ついうっかり」を防ぐためにもユニットテストを書くべきですが、それはまた別の話として。

NaN(Not A Number)は数値なの?

NaN は Not a Number の略です。「数値ではない」という意味です。判定するにはグローバルコンテキストの isNaN という関数が使えます。

しかし NaN の型名は number ですし、コンストラクタは Number なのです。 何かスッキリしないものが残ります。

> a = NaN
NaN
> isNaN(a)
true
> typeof a
'number'
> a.constructor.name
'Number'
>

まあ、あまり多用する言語要素でないし、使うときはきっちり調べてコーディングするので、非効率ですが問題になりにくいのが救いかも。

Array#reverse は配列自体を変化させる

Array#reverse は配列を逆順にするためのメソッドです。 しかし自分は「Array#reverseは配列自体を変化させずに、新しい配列を返す」と思いこんでいたのですね。 実際、このメソッドの戻り値は逆順になった配列なのですが、まさか配列自体が逆順になって return this; されているとは思っていなかった。 「ホントかよ!?」みたいな。 困りものです。

REPLで確認してみると、明らかに配列自体が逆順になっていました。

$ node
Welcome to Node.js v12.18.3.
Type ".help" for more information.
> a = [1,2,3,4]
[ 1, 2, 3, 4 ]
> a.reverse()
[ 4, 3, 2, 1 ]
> a
[ 4, 3, 2, 1 ]
>

ややこしいのは、reverse だけでないのです。reverseのように配列自体を変化させてしまうメソッドや、配列自体は変化させず、別の配列を返してくるメソッドが、Arrayクラスには混在してます。 しかし、メソッド名を見ても、その挙動がわからない。

いちいちリファレンスを参照しないと不安ですよね。 全部覚えちゃうわけにも行きませんし。 こういうところで生産性が下がるのね。

まあ、よく使うものは覚えていますが、体調が悪いときや睡眠不足のときなどにはわけがわからなくなってしまったり。 割と大きな問題かもなと思います。

まとめ

それぞれJavaScriptの歴史的な事情が関わっていると思います。1990年代から2005年ぐらいまではブラウザの添え物的で、プラットフォーム依存がひどかった。

その後、ブラウザ間の実装の違いを吸収するライブラリが開発され、ECMAScriptとしての統一仕様の策定、Ajaxの勃興、Node.jsの登場、HTML5の策定など重要なイベントがたくさん有って、しかも、ほとんど下位互換性を保ちながら進化してきたのです。

こういった歴史を知っているなら「仕方がないね」と思えますけど、今から使おうとする人には敷居が高すぎるんじゃないかなと。 こういった論理的でない挙動が参入障壁になっていたり、JavaScriptのイメージが悪化につながっているのではないだろうかと、要らぬ心配をしてしまう。 特にビギナーに説明するには、それなりの時間を使いますし、心理的に「なんやようわからん」となりがちだろうなと。

プログラミング言語の人気投票的な結果ではJavaScriptは割と上位に来ますし、多く使われているのも事実だとは思います。 なんか良い方法ないですかね。