すでに「式」と式が返す値についていくつか例を見たが、さらに、落とし穴になりがちな例を見ておこう。まず、前提として式は値を返すことを理解しておく必要がある。当たり前のような話だが、きちんと確認しておこう。
変数やリテラルがその値を返したり、算術演算子を使って書かれた式が計算の結果を返したりすることは想像に難くない。しかし、演算子によっては日常の感覚と異なるところもある。例えば、「=」による代入では、左辺に代入された値(つまり右辺の値)が返される。代入では変数に値が入れられるだけではなく、値が返されるのである。
一般的には、演算とは何らかの計算をして、その結果を式の値として返すことを言います。主たる働きはあくまで式の値を返すことであって、代入などによって変数の値が変わることは「副作用」と呼ばれます。例えば、「=」という演算子の本来の機能は右辺の値を返すことであって、左辺の変数の値が変わることはオマケのようなものなのです。
従って、「代入では変数に値が入れられるだけではなく、値が返される」という表現よりも「代入では値が返されるだけではなく、変数に値が入れられる」といった方が正しい表現になるわけです。日常の感覚とはずいぶんと異なりますが、私たちは「副作用」を利用して変数の値を変え、作業をしているわけです。
特に、比較演算子の使い方には注意が必要である。例えば「変数ageの値が18以上、25未満」という条件を表すとき、日常の感覚では普通、以下のように書く。
18 <= age < 25
しかし、これはTypeScriptではエラーになる。ところが、困ったことに、JavaScriptではこれがエラーにならないばかりか、誤った結果を返してしまう。以下のプログラムを入力して実行してみよう。
var age: number;
age = 30;
if(18 <= age < 25) { // ageが18以上、25未満なら
alert("範囲内です"); // 「範囲内です」と表示する
}
Playgroundでこのコードを入力すると、TypeScriptの側ではエラーを示す赤い波線が表示される。しかし、JavaScriptのプログラムは作られるので、実行はできる。[Run]ボタンをクリックして実行してみると、明らかに30というのは範囲外なのに「範囲内です」と表示されてしまうのだ。
比較演算子は、いわば条件を表すもので、その条件が成り立っていればtrueを返し、成り立っていなければfalseを返す。大小比較の演算子は優先順位が同じで、結合方向が「→(左から右)」なので、「18 <= age < 25」では、「18 <= age」という演算がまず実行される。変数ageの値が30なら、結果は当然trueになる(この式がtrueという値を返す)。続いて、「true < 25」という演算が実行される。TypeScriptでは、データ型が異なるので、これはエラーになるのだが、JavaScriptではtrueは1、falseは0に変換されて計算されてしまうので、「1 < 25」という演算が実行される。1は25より小さいので、この式はtrueを返し、期待したのとは異なる結果になってしまうというわけだ。
複数の条件を調べたいときには、論理積を求める「&&」演算子や論理和を求める「||」演算子を使う。||は全角の「‖」ではなく、半角の「|」が2つであることに注意しよう。先ほどの例を正しく書き替えると以下のようになる。
var age: number;
age = 30;
if(age >= 18 && age < 25) { // ageが18以上、25未満なら
alert("範囲内です"); // 「範囲内です」と表示する
}
今度はプログラムが正しく実行されるはずだ(実行しても、変数ageの値が範囲外なのでメッセージは何も表示されない)。
日常の感覚と異なる例を仕組みから理解していくことは、本質的な理解につながるので、よくある落とし穴の例をもう1つ見ながら、演算や式が返す値について考えてみよう。
インクリメントやデクリメントのための演算子は、変数の前に書く(前置)か、後ろに書く(後置)かによって式が返す値が異なる。
では、以下のプログラムを見て、結果が10と表示されるか11と表示されるか予想してほしい。
var x:number = 10
var y;
y = x++; // (1)
alert(y);
(1)で変数xの値をインクリメントしていることは簡単に分かる。問題は変数yにどんな値が代入されるかだ。以下のように考える人もいるかもしれない。
しかし、実際には「10」が表示される。
なぜか?
これを、「++」が前にあると先にインクリメントされてから代入されるが、後ろにあると(演算子の優先順位はこのときだけ例外扱いされ)代入されてからインクリメントされる、と教わった人も多いかもしれない。結果はそれで合っているのだが、実はそういうわけではない。
「++」が前置されたときは、インクリメントされた値が式の値として返されるが、後置されたときはインクリメントされる前の値が式の値として返される。返される値が異なるだけのことで、インクリメントが実行された後、式の値が代入される。
変数の値が、必ずしも式の値ではないということに注意しよう(前のコラムでも述べたように、変数の値が変わるのは副作用なのだ!)。演算子の優先順位に例外があるわけではなく、いずれの場合も、ちゃんと++演算子が先に実行されている。返される式の値が異なるというだけのことだ。
ともあれ、こういった考え方を知ると、表面的な使い方にとどまらず、より深い理解に到達できる。すぐに役に立つ知識ではないが、先に進めば進むほど役に立つ知識なので、確実に理解しておこう。また、時には、言語仕様を見ながら今回のサンプルのような簡単な実験をして、疑問を解消する習慣を付けるのもいいだろう。
今回はTypeScriptにおける式と演算子について、基本的な使い方や考え方を紹介した。比較演算子については、落とし穴だけを紹介したが、条件分岐のif文などと合わせて使うことが多い。そこで、次回は条件分岐をメインのテーマとして、比較演算子や条件演算子についてもさらに説明を加えたいと思う。
Copyright© Digital Advantage Corp. All Rights Reserved.