【JavaScript】コードの見通しを良くする為に意識している事10選
普段コード書いている中で見通し(可読性)を良くするために意識していることを10個列挙しました
JavaScript(TypeScript)のお話です
1.変数宣言はconstを利用する
変数を宣言するときはconst
を使います。var
は使わない様にします
let
を使うときは限定的で、実行する関数の中で変数を再代入する必要がある時に使います
// 関数の外でのlet宣言はアンチパターン
let strValue = "";
const method1 = () => {
strValue = "method1";
};
const method2 = () => {
// 関数内、再代入する処理で宣言する
let setValue = "ほげ";
};
// 以下、変数strValueを利用するかもしれない
// どこかで再代入されるかも分からない..
let
は処理の途中で変数の値が変わる事を暗に示しているので、読む人によっては苦痛になります
再代入する必要がない場合は不必要にlet
を使う理由はないで、まずconst
を書く様に意識しています
2.省略して書く
true,falseは省略して書きます
// よくない
if(checkBoolean === true)
if(checkBoolean === false)
// 省略できる
if(checkBoolean)
if(!checkBoolean)
比較する対象がundefined
、null
、""
の場合は!!
に省略できます
省略しない場合↓
const hoge = "example";
const hogeFunc = (hoge) => {
return hogehoge !== undefined &&
hogehoge !== null &&
hogehoge !== ""
};
console.log(hogeFunc(hoge))
省略した場合↓
const hoge = "example";
const hogeFunc = (hoge) => {
return !!hoge
};
console.log(hogeFunc(hoge))
!!
をつけると、あらゆる型がbooleanになります(キャストされる)
特にundefined
についてはhoge !== undefined
では誤った判定をされる可能性もあり推奨されていないので、!!
を使うようにします
3.三項演算子
以下の書き方(三項演算子)でif
を省略して書くことができます
条件式 ? 真の場合の値 : 偽の場合の値;
もちろん、三項演算子で評価した結果を代入する事もできます
const 変数 = 条件式 ? 真の場合の値 : 偽の場合の値;
この三項演算子ですが、書き方によっては2種類の書き方ができます
1つ目は、評価した結果をそれぞれ代入するパターン↓
const TRUE_MESSAGE = "trueです";
const FALSE_MESSAGE = "falseです";
const isCheckBoolean = true;
const resultList = [];
isCheckBoolean ? resultList.push(TRUE_MESSAGE) : resultList.push(FALSE_MESSAGE);
console.log(resultList); // ['trueです']
2つ目は、評価した結果を左辺に代入するパターン↓
const TRUE_MESSAGE = "trueです";
const FALSE_MESSAGE = "falseです";
const isCheckBoolean = true;
const resultList = [];
resultList.push(isCheckBoolean ? TRUE_MESSAGE : FALSE_MESSAGE);
console.log(resultList); // ['trueです']
どちらも結果は全く同じですが、可読性という意味では、宣言した変数を一度書けば済むパターン2の方が個人的には読みやすいです
Boolean()
で書く
4.下記の様に、処理の中で何もせずに単純にreturn ture
またはreturn false
で返している書き方は冗長です
// よくない
(() => {
const checkString = ""
if(checkString) {
return true
} else {
return false
}
})();
// 省略できる
(() => {
const checkString = ""
return Boolean(checkString)
})();
三項演算子も評価値がBooelanであれば〜 ? true : false
と書くのは冗長です
const checkString = ""
// よくない
const checkHoge = checkString ? true : false
// Booleanで書く
const checkHoge = Boolean(checkString)
5.return
関数の処理の中でreturn
すると、それ以降は実行されません
if
で条件を満たしていない時などに後続を実行させたくない時によく使います
(() => {
const checkBoolean = true
if(checkBoolean) return // tureの場合は後続の処理は実行されない
// falseの場合だけ実行
console.log(checkBoolean);
})();
else 〜
と書く必要がないので、記述量が減ります
6.オプショナルチェーン
オプショナルチェーン演算子はES2020で追加されました
JavaScriptでは、プロパティの値がないものにアクセスしようとするとエラーになってしまいます
(よく見るUncaught ReferenceError: 変数名or関数名 is not defined
)
このエラーに当たると、プログラムが止まってしまい非常に厄介です
対処法としてif
でプロパティがあることを確認してからアクセスするのが定石ですが、冗長になってしまいます
const freamWork = {
name: "react",
version: "18",
}
// オプショナルチェーンを使わない場合
if (freamWork && freamWork.version) {
const getVersion = freamWork.version;
console.log(getVersion);
}
オプショナルチェーンを使うと、たとえ存在しないプロパティにアクセスしてもエラーにならず、undifined
を返してくれます
const freamWork = {
name: "react",
version: "18",
}
// オプショナルチェーンを使う場合
const check = freamWork?.manual
console.log(check) // undifined
利用場面としてはサーバサイドからfetch
したデータに対してよく使います
7.スプレッド構文
スプレッド構文はES2015(ES6)に追加された構文で、配列やオブジェクトを展開してくれます
下の例では、スプレッド構文を使ってオブジェクトを展開することで、一つのまとまったオブジェクトを作成しています
const userInfo = {
user_id: 1234,
user_name: 'naru',
user_email: 'naru@gmail.com',
}
const brouserInfo = {
browser: 'chrome',
version: '110'
}
const add = "example"
// 一つのオブジェクトにまとめています
const sumArray = {add, ...userInfo, ...brouserInfo}
console.log(sumArray)
// {add: 'example', user_id: 1234, user_name: 'naru', user_email: 'naru@gmail.com', browser: 'chrome', …}
上記の様に処理の中でオブジェクトを1つにしたい時や、バックエンドへ送るパラメータを一つにまとめたい時によく使います
8.Setオブジェクト
Setオブジェクトは、ES2015(ES6)で追加されました
new Set()
を使うと、値が被らないオブジェクトを作成してくれます
const numArray = ["2","1","3","2","1","5","6","7","8"]
const result = new Set(numArray)
console.log(result)
// 結果
// Set(7) {'2', '1', '3', '5', '6', …}
また、スプレッド構文と組み合わせる事で、配列も作れます
const numArray = ["2","1","3","2","1","5","6","7","8"]
const result = [...new Set(numArray)]
console.log(result)
// 結果
// (7) ['2', '1', '3', '5', '6', '7', '8']
new Setしたオブジェクトに対しては、add()
メソッドで追加する事ができます
重複しているかの確認のif
を書く必要がない、同じ値を追加してもエラーにならずに一意にしてくれるのでかなり使い勝手がいいです
const labels = new Set();
labels.add("apple");
labels.add("apple");
const labelsList = labels; // Set(1) {'apple'}
// 配列にしたい場合はスプレッド構文を使えばOK
const labelsArrayList = [...labels]; // ['apple']
また、応用して2つの配列を比較したりするときに便利です
const list_A = [3,5,7];
const list_B = [2,6,9];
// スプレッド構文を利用して、配列を一つにまとめる
const checkSumList = [...list_A, ...list_B];
// 一意にする(被りをなくす)
const resultSumList = [...new Set(checkSumList)];
// 重複がある場合は配列の要素の数は減るのでこれでチェックできる
if (resultSumList.length === checkSumList.length) {
// 重複がない時の処理
console.log("重複はありません")
} else {
// 重複があった時の処理
console.log("重複しています")
}
9.includes()
includes()
は指定された要素が配列に含まれているかどうかを調べて、あればtrue
なければfalse
を返すメソッドです
ECMAScript 2016 (ES7)で追加されました
例えば、"iPhone"の時はtrue
それ以外の時はfalse
に判定したい処理があるとします
よくある書き方↓
const DEVICE = "iPhone"
if(DEVICE === "iPhone"){
// trueの時の処理
}
// falseの時の処理
このような場合はincludes
を使うのが便利です
まず"iPhone"を配列に収めます
1個しかない場合でも配列に入れるのがポイントです
定数化して/util
等にまとめておきます
// exportでモジュール化
export const DEVICE_LIST = ['iPhone'];
使う時はインポートします
下記の様にincludes
を使って変数に定数の値があるかないかチェックすることができます
// 上記をインポート
import DEVICE_LIST from "上記のファイルパス";
// includesが便利
const check_device = FRUITS_LIST.includes("iPhone");
if(check_device){
// trueの時の処理
}
includes
の良いところは、配列で何をtrue判定させたいのかの管理ができるところです
要は後で追加・削除がしやすくなります
例えば、追加で"mac"もtrue処理したい場合は、以下の様に配列に入れればOKです
export const DEVICE_LIST = ['iPhone', 'mac'];
こんな感じで、決められたものに対してチェックをしたい場合にincludes()
は使いやすいです
10.map()、filter()
- map() ECMAScript 5 (ES5)で追加
- filter() ECMAScript 5 (ES5)で追加
map()
やfilter()
を使うことでforEach
を使わなくても簡潔にコードを書く事ができます
forEach
では、ぱっと見だと何をしているコードか分かりづらくなりがちです
// forEachを使った場合
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = [];
numbers.forEach((num) => {
doubledNumbers.push(num * 2);
});
console.log(doubledNumbers); // [2, 4, 6, 8, 10]
// mapを使った場合
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map((num) => {
return num * 2;
});
console.log(doubledNumbers); // [2, 4, 6, 8, 10]
map()
で実現できるのであればforEach
より優先して使っています
なおfilter()
の便利な使い方として、値が""
など空の値があるときに.filter(v => v);
とすることで、空白を取り除く事ができます
filter()
を使わない場合↓
const numbers = [1, 2, "", 4];
const doubledNumbers = numbers.map((num) => {
return num * 2;
});
console.log(doubledNumbers); // [2, 4, 0, 8] 空白の処理で0が入ってしまう
filter()
を使った場合↓
const numbers = [1, 2, "", 4];
const doubledNumbers = numbers.filter(v => v).map((num) => {
return num * 2;
});
console.log(doubledNumbers); // [2, 4, 8]
またmap()
やfilter()
で作成した配列は、元の配列とは別の配列として作成されるため、元の配列に影響を及ぼしません。forEach
よりかは扱いやすいです
ただし、注意点としてfilter()
で1回、map()
で2回ループしている事になるので、ある意味無駄に処理しているとも言えます
Setなど他のやり方を検討しつつ、使い所では(処理量が少ないとか)積極的に使っていいと思います
最後に
注意を払っておりますが、間違い等ありましたら随時修正します
Discussion
constで宣言した値に直接代入してはエラーが出ると思われます。
配列なのでpushや引数を使って代入する方が良いかなと思います。
ご指摘ありがとうございます。
おっしゃる通り、
const
で宣言した配列に直で値を入れようとしてますね...push()
で配列に値を追加する形に修正しましたBoolean(foo)
は二重否定を使って!!foo
とすることが多いですねコメントありがとうございます
ご指摘頂くまで二重否定を意識的に使った事がなく、Booleanを多用しておりました
こちら大変勉強になりました(MDNのリンクも添付頂きありがとうございます)
本文にも追記しました
後学のために教えて頂きたいのですが、
hoge !== undefined
で誤った判定をされるのはどのような場合なのでしょうか??コメントありがとうございます
私の認識ですが、以下の認識で記述しています
JavaScriptでは
undefined
をグローバルな"変数"として扱います(変数というのがポイントです)
変数である以上、数字(100)や文字列("hoge")等の
false
と判定されない値も代入ができる訳ですなので、
undefined
と比較するつもりでhoge !== undefined
と書いても、数字(100)や文字列("hoge")と比較することになり、true判定されてしまう....という事もあり得る。という意味で書きました
ただ上記の事を言いながら、最近のブラウザでは
undefined
を代入したり上書きする事を禁止しているので(上記の話はJavaScriptの言語仕様のお話です)あまり気にする必要もないのが正直な所です
ご丁寧に返信頂きありがとうございます。↑の仕様ですね、私は気にしたことなかったです…
kata様が言及されているケースについて、理解できました、ありがとうございます。
自分も以前にfilterしてからmapするという処理をしたときに
別の観点から違う書き方を知ったのでここにも記載します
ES2019から追加されたflatMapを使用することで記事の以下の記載
を解決してループ1回で処理ができます。
しかし本記事の主題である可読性から言うと慣れてないと??となる点、フィルターしてからマップする!のほうが圧倒的にわかりやすさと可読性が両立してるのでケースバイケースかなと思いました。
あと
filter()を使わない場合↓
とfilter()を使った場合↓
のコメントで[2, 4, 0, 10]
及び[2, 4, 10]
となっているところは[2, 4, 0, 8]
及び[2, 4, 8]
かなと思いますBoolean()で書くや二重否定は全く知らなかったので大変勉強になりました!
コメントありがとうございます
仰る通り、分かりやすさの重視で書きました
しかし
flatMap()
がと考えると
flatMap()
でも十分に可読性があると認識しております以上の事を考えると、両方のパターンを書くべきでした
ご指摘を頂いた事を明記し、追記させて頂きました。ありがとうございます
なお別論点での気づき、ですが
この様な形で、いつからメソッドが使える様になった点も明記して頂き大変参考になりました
記事全体を見直し追記しようと思います
こちらも修正しました、ありがとうございます