どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。今日は第6回目です。
前回のおさらい
前回は、「オブジェクトラッパーよりもプリミティブが好ましい」をお届けしました。
今回は、「型が異なるときに==を使わない」 をお届けします。
テキスト第1章「JavaScriptに慣れ親もう」の項目5に対応しています。
今日のアジェンダ
- 強制とコーディングガイドライン
- リーダブル厳密等価演算子
- 等価演算子の強制ルール
強制とコーディングガイドライン
項目3で、「暗黙の型変換に注意しよう」をお届けしました。
JavaScriptでは、期待される型に合わせ、内部的に値を変換することを 「強制(Coercion)」 と呼びます。
項目3では、さまざまな強制と注意事項をまとめましたが、「では、どの書き方が正解なのか」 には触れませんでした。
項目4でプリミティブ値の生成にも触れましたし、ここらで 文字列型と数値型のコーディングガイドライン を考えてみましょう。
リテラルのコーディングガイドライン
まず、「リテラルが用意されている型は、リテラルを使う」 というルールは有益です。
new演算子が用意されているオブジェクトであろうと、リテラルを優先し、new演算子は禁止としてよいでしょう。
function myFunction1_5_01() {
//リテラルが用意されているオブジェクトはリテラルで書く
console.log(new String('Tom')); //{ '0': 'T', '1': 'o', '2': 'm' }
console.log('Tom'); //Tom
console.log(new Number(10)); //{}
console.log(10); //10
console.log(new Boolean(true)); //{}
console.log(true); //true
console.log(new Array(1, 2, 3)); //[ 1, 2, 3 ]
console.log([1, 2, 3]); //[ 1, 2, 3 ]
console.log(new Object()); //{}
console.log({}); //{}
console.log(new RegExp('.*?')); ///.*?/
console.log(/.*?/); ///.*?/
}
変換と強制のコーディングガイドライン
暗黙的に行われる型変換は「強制」ですが、プログラマが型の変換していることを、コードで明示的に示した方がいいばあいもあるでしょう。
普段、なんとなく使えているtoString()メソッドや、parseInt()メソッドも例に挙げてみました。
Myコーディングガイドラインを作成することで書き方を統一し、リーダブルコードを心がけましょう。
function myFunction1_5_02() {
//数値型から文字列型への変換と強制
const num = 10;
//好ましくない書き方
console.log(new String(num)); //{ '0': '1', '1': '0' }
console.log(num + ''); //'10'
console.log(num.toString()); //'10'
//好ましい書き方
console.log(String(num)); //'10'
//文字列型から数値への変換と強制
const text = '10';
//好ましくない書き方
console.log(new Number(text)); //{}
console.log(+text); //10
console.log(parseInt(text)); //10
//好ましい書き方
console.log(parseInt(text, 10)); //10
console.log(Number(text)); //10
}
リーダブル厳密等価演算子
JavaScriptでは、値だけでなく、型の判定まで比較する、厳密等価演算子(===)が提供されています。
Dataオブジェクトから取得した月日(数値型)と、JSON(文字列型)を比べるばあいなどは、型を揃えてから厳密等価演算子で比較しましょう。
厳密等価演算子であれば、コードを読む人が、余計な不安を抱えることがありません。「コードが増える」と言っても、たった2行です。
「処理をする」ことよりも「読まれる」ことを優先した(ただしく処理が行われることは当然です)、まさにリーダブルコードだと思います。
function myFunction1_5_03() {
const json = `{
"birthday_year": "1980",
"birthday_month": "08",
"birthday_day": "12"
}`;
const today = new Date();
const month = today.getMonth() + 1;
const day = today.getDate();
const jsonObject = JSON.parse(json);
const usersBirthdayMonth = jsonObject.birthday_month;
const usersBirthdayDay = jsonObject.birthday_day;
//厳密等価演算子では処理できない
if (month === usersBirthdayMonth && day === usersBirthdayDay) {
console.log('HappyBirthDay!!!');
} else {
console.log('今日は誕生日ではありません。'); //今日は誕生日ではありません。
}
//等価演算子なら正しい判定を行う
if (month == usersBirthdayMonth && day == usersBirthdayDay) {
console.log('HappyBirthDay!!!'); // 'HappyBirthDay!!!'
} else {
console.log('今日は誕生日ではありません。');
}
//型をそろえて、厳密等価演算子で記述しよう
const userBDMonth = Number(usersBirthdayMonth);
const uesrBDDay = Number(usersBirthdayDay);
if (month === userBDMonth && day === uesrBDDay) {
console.log('HappyBirthDay!!!'); // 'HappyBirthDay!!!'
} else {
console.log('今日は誕生日ではありません。');
}
}
等価演算子の強制ルール
ここまで強く、厳密等価演算子で書くことを推奨するのは、等価演算子を使用したさいの強制ルールが不明瞭 だからです。
valueOf()とtoString()メソッドが両方定義されているオブジェクトでは、暗黙の型変換のさい、valueOf()メソッドを優先的に呼び出し ていました。(テキストP13)
では、if(’10’ == new Number(’10’)){} というステートメントでは、数値型と文字列型のどちらで判定するのでしょうか。
正解は、「数値型」です。
テキストでは、Dateオブジェクトを例に、「等価演算子の強制により型がどうなっているかを、コードを読む人の負担にすべきではない」 と言っています。
function myFunction1_5_04() {
//オブジェクトに定義されている2つのメソッド
console.log(new Number('10').valueOf()); //10 number
console.log(new Number('10').toString()); //'10' string
//等価演算子の型強制
if ('10' == new Number('10')) console.log('両辺とも数値型に強制され比較される');
//等価演算子の強制ルール
if (null == undefined) console.log('強制はなし。常にtruthy');
if (null || undefined == !null && !undefined) {
} else {
console.log('強制はなし。常にfalsy');
}
//Dateオブジェクトはプリミティブに強制される
console.log(new Date('2021/8/12').toString());//Thu Aug 12 2021 00:00:00 GMT+0900 (Japan Standard Time)
console.log(new Date('2021/8/12').valueOf()); //1628694000000
if ('1628694000000' == new Date('2021/8/12')) console.log('今日の天気は?');
if ('1628694000000' == new Date('2021/8/12').valueOf()) console.log('今日はいい天気ですね');
}
Dateオブジェクトであれなんであれ、オペランドを比較するさいは、型を揃えて厳密等価演算子で評価しましょう、というお話でした。
※今回は、実践的な内容が多かったので、「実務を意識するなら」はおやすみしますzzz
まとめ
以上で、「型が異なるときに==を使わない」をお届けしました。
条件式では積極的に===を使い、できるだけ==を使わないように注意してきたわたしですが、「==で判定できないときは明示的に型を変換しよう」 というのは新たな発見でした。
次回は、「セミコロン挿入の限度を学ぼう」 をお届けします。
参考資料
このシリーズの目次
[EffectiveJavaScript輪読会]ノンプロ研EffectiveJavaScript輪読会とは
[EffectiveJavaScript輪読会]どのJavaScriptをつかっているのかを意識しよう
[EffectiveJavaScript輪読会]JavaScriptの浮動小数点を理解しよう
[EffectiveJavaScript輪読会]暗黙の型変換に注意しよう
[EffectiveJavaScript輪読会]オブジェクトラッパーよりもプリミティブが好ましい
[EffectiveJavaScript輪読会]型が異なるときに==を使わない
[EffectiveJavaScript輪読会]セミコロン挿入の限度を学ぼう
[EffectiveJavaScript輪読会]文字列は16ビットの符号単位を並べたシーケンスとして考えよう