JavaScript(TypeScript)です。
読みやすい if
文
比較したい変数は比較演算子の左側に書く
以下のコードでは「年齢が10歳以上か」と自然に読むことができます。
if (age >= 10) { }
「10歳が年齢以下か」とは人間の思考的には考えにくいです。
if (10 <= age) { }
範囲内にあるかないかを調べるには数直線上に並べる
数直線上に並べると視覚的にわかりやすくなります。
// 1~100のランダムな値を得る
const getRandomScore = (): number => Math.floor(Math.random() * 101);
const value = getRandomScore();
// 以下の様に書けば視覚的にすぐに分かる
if (20 <= value && value <= 80) {
console.log(`値は範囲内です:${value}`);
} else {
console.log(`値は範囲外です:${value}`);
}
比較対象が常に左にあると視覚的にわかりづらくなります。
if (value >= 20 && value <= 80) { }
**true
やfalse
**と比較しない
if (word.includes('foo') /*文字列にfooが含まれるか*/) {
// 含まれる場合の処理...
}
真偽値を返すメソッドやプロパティを判定する際、わざわざ**true
(false
**)と比較するのは冗長です。
if (word.includes('foo') === true) { }
早期リターンやガード節を使う
条件を満たさないケースを**return
**文を使って早めにふるい落とします。
条件を忘れていくことが可能なので脳内メモリにやさしいです。
const isValid = (model: Model) => {
if (model === null) {
return false;
}
if (model.name.trim() === "") {
return false;
}
if (model.name.length > 20) {
return false;
}
if (!(0 <= model.age && model.age <= 150)) {
return false;
}
if (!/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(model.email)) {
return false;
}
return true;
};
const isPrime = (num: number): boolean => {
if (num <= 1) {
return false;
}
if (num === 2) {
return true;
}
const boundary = Math.floor((Math.sqrt(num)));
for (let i = 0; i <= boundary; ++i) {
if (num % i === 0) {
return false;
}
}
return true;
};
以下のように**return
**文を一つにするとネストがどんどん深くなります。
さらに、コードを読む際に条件を全ておぼえておかなければなりません。
途中で条件を加えるたびにコードはますます複雑になります。
const isValid = (model) => {
let blnrtn = false;
if (model != null) {
if (model.name.trim !== "") {
if (model.name.length <= 20) {
if (mailPattern.test(model.email)) {
blnrtn = true;
}
// if …
}
// if …
}
}
return blnrtn;
};
上記のように「奥へ突き進む」型のコードならまだわかりやすいですが、
ブロックを閉じる位置などにif文等が加わると保守しにくいコードになっていきます。
また、以下のように return
を無理やり一つにしようとすると余計なフラグ変数を用意しなければならなくなったり、ネストが複雑になります。
const isPrime = (num) => {
let blnrtn = true;
if (Number.isNaN(num)) {
blnrtn = false;
} else if (num <= 1) {
blnrtn = false;
} else if (num === 2) {
blnrtn = true;
} else {
const boundary = Math.floor((Math.sqrt(num)));
for (let i = 0; i <= boundary; ++i) {
if (num % i != 0) {
blnrtn = true;
break;
}
}
}
return blnrtn;
};
即時関数を使う
即時関数を使うことでメソッド内に変数のスコープを限定出来ます。
(即時関数・・・匿名メソッドを定義してそのまま実行することを一般的に即時関数と呼ぶようです。)
// ランダムなスコア(1~100)を得る
const getRandomScore = (): number => Math.floor(Math.random() * 101);
const rank: string = ((score) => {
if (score >= 80) {
return 'A';
} else if (score >= 60) {
return 'B';
} else if (score >= 40) {
return 'C';
} else {
return 'D';
}
})(getRandomScore());
console.log(rank);
以下のように書くと score
のスコープがグローバルになってしまい、 rank
も定数でなくなったために、修正を繰り返すうちに再代入されて思いもよらない値が入る可能性があります。
さらに、リファクタリングを繰り返すうちに元の目的とは関係のないのコードが if
文にまぎれ可読性を損なうことがあります。
const score = getRandomScore();
let rank = '';
if (score >= 80) {
rank = 'A';
// ~~~~ ランク判定とは関係のないコード ~~~~
} else if (score >= 60) {
rank = 'B';
} else if (score >= 40) {
rank = 'C'
// ~~~~ ランク判定とは関係のないコード ~~~~
} else {
// ~~~~ ランク判定とは関係のないコード ~~~~
rank = 'D';
}
score = 10; // ここでもアクセスできてしまう。
// ~~~~~~~~何か長い処理 ~~~~~~~~~
rank = 'S'; // 意図せぬ値が再代入
console.log(`${rank}です。`);
そもそも if 文を書かない
真偽値は直接それを返す
2つの値をその結果を**true
かfalse
**で返したい場面があります。
その場合、評価した結果を直接返します。
return foo === bar;
以下のようにフラグ変数を用意したり**if
**文を使用するのは冗長です。
const flag = (foo === bar);
return flag;
if (foo === bar) {
return true;
} else {
return false;
}
三項演算子を使う
三項演算子を使うと余計なローカル変数を定義しなくても良く、if
文と違って余計なコードがまぎれる心配もありません。
const time: number = new Date().getHours();
const ampm: string = time <= 12 ? '午前' : '午後';
console.log(`今は${ampm}です。`);
三項演算子は式なので直接、関数の引数に書くことも出来ます。
以下の例は配列 words
に文字列 'foo'
が含まれている場合に1を、そうでない場合に0を引数に渡す例です。
class Foo {
static doSomething(n: number): string {
return `${n} が引数として渡されました`;
}
}
const words: string[] = ['foo', 'bar', 'buz'];
const result: string = Foo.doSomething(words.some(word => word === 'foo') ? 1 : 0);
console.log(result); // 1 が引数として渡されました
配列の反復メソッドを使う
以下のような Book
クラスとその配列の myBookShelf
があったとします。
class Book {
constructor(
public name: string,
public price: number
) { }
public includedTax(): number {
return Math.floor(this.price * 1.08)
}
}
const myBookShelf: Array<Book> = [
new Book('JavaScript入門', 1750),
new Book('基礎からのTypeScript', 3200),
new Book('AnglarとTypeScriptでアプリ製作', 2550),
new Book('実践! Node.js', 2980),
// ...以下略
];
myBookShelf
に税込み価格2000円以上の本がいくつあるかを求めます。
for
ループとif文を組み合わせた方法で求めようとすると以下のようになります。
let count: number = 0;
for (const book of myBookShelf) {
if (book.includedTax() >= 2000) {
count++;
}
}
console.log(`私は税込み2000円以上の本を${count}冊持っています。`);
配列の反復メソッドを使用すると以下のように簡潔に記述できます。
何がしたいのかが一目でわかります。
const count: number = myBookShelf
.filter(book => book.includedTax() >= 2000)
.length;
console.log(`私は税込み2000円以上の本を${count}冊持っています。`);
Array
には以下のように if
を使わずに読みやすく書けるメソッドが用意されています。
-
filter
条件に合うものだけフィルタリングした配列を返す -
find
配列内の条件を満たす最初の要素を返す -
every
全ての要素が条件を満たすかをtrue
またはfalse
で返す -
includes
特定の要素が配列に含まれているかどうかをtrue
またはfalse
で返す -
some
配列内に条件を満たす要素があるかをtrue
またはfalse
で返す
以上です。ここまで読んでいただきありがとうございました。