こちらの記事の続編になります。
7章 制御フローを読みやすくする(83p~)
鍵となる考え
- 条件やループの制御フローはできるだけ「自然」にする
- コードの読み手が立ち止まったり読み返したりしないように書く
7.1条件式の引数の並び順
- if(10 <= length)
+ if(length >= 10)
左側 | 右側 |
---|---|
調査対象の式(変化する) | 「比較対象」の式。あまり変化しない。 |
7.2if/elseブロックの並び順
- 条件は肯定形を使う
- 単純な条件を先に
- 重要度の高い条件を先に
- if(a !== b){
- //第2のケース
- }else{
- //第1のケース
- }
//上下は同じ意味
+ if(a === b){
+ //第1のケース
+ }else{
+ //第2のケース
+ }
7.3三項演算子
三項演算子の方が読みやすい
+ const timeStr = hour >= 12 ? "pm" : am
- if(hour >= 12 ){
- timeStr += "pm"
- }else{
- timeStr += "am"
- }
通常のifの方が読みやすい
- return exponent >= 0 ? mantissa * exponent : mantissa / -exponent ;
+ if(exponent >= 0 ){
+ return mantissa * exponent
+ }else{
+ return mantissa / -exponent
+ }
- 行数を短くするよりも他の人が理解するのにかかる時間を短くする
- 基本的にはif/else。三項演算子はそれによって簡潔になるときだけ
7.4do/whileループを避ける
リーダブルコードではdo/whileになっているが
ここではreactにおけるmapの無限ループの悪い例をあげる
function BadExample() {
const [items, setItems] = useState([1, 2, 3]);
useEffect(() => {
// itemsを更新する際、新しい配列にmapで加工を行う
// この処理がitemsの変更を引き起こし、それが再度このuseEffectをトリガーする
// 依存配列にitemsを含めているため、itemsが更新されるたびにこの副作用が実行される
setItems(items.map(item => item * 2));
}, [items]); // 依存配列にitemsを含めることで、itemsの更新時にこのuseEffectが再実行される
return (
<div>
{items.map(item => (
<div key={item}>{item}</div>
))}
</div>
);
}
改善例
①条件をつける
useEffect(() => {
if (shouldUpdate) {
// 条件に基づいてitemsを更新する
setItems(items.map(item => item * 2));
// 一度更新した後は、無限ループを避けるために更新フラグをfalseに設定
setShouldUpdate(false);
}
}, [items, shouldUpdate]); // shouldUpdateを依存配列に追加
②初回レンダリングのみで回避
useEffect(() => {
// 初回レンダリング時にのみ実行される副作用
setItems(items.map(item => item * 2));
}, []); // 依存配列を空にする
7.5関数から早く返す
関数から早く返すのはいいこと
export const checkType = (params) => {
const [first, second, third, fourth] = params;
// 拡張子付きのパスは無視
// - /sw.js
// - /favicon.ico
if (first.includes('.')) {
return false;
}
const isFirstYyyy = first.match(/^\d{4}$/);
const isSecondMm = second?.match(/^\d{2}$/);
const isThirdDd = third?.match(/^\d{2}$/);
const isFirstString = !isFirstYyyy;
const isSecondString = !isSecondMm && second !== undefined;
if (
isFirstYyyy &&
(isSecondMm || second === undefined) &&
(isThirdDd || third === undefined) &&
fourth === undefined
) {
return 'list';
}
if (isFirstYyyy && isSecondMm && isThirdDd && fourth !== undefined) {
return 'post';
}
if (isFirstString && (isSecondString || second === undefined)) {
return 'page';
}
return false;
};
7.6悪名だかきgoto
gotoとは(chatGPTに聞いてみた)
gotoはプログラミング言語における制御文の一種で、プログラムの実行を指定されたラベルが付けられたプログラム内の別の位置へ無条件に移動させるために使用されます。この機能により、通常の上から下への逐次実行フローを中断し、指定されたポイントへプログラムの実行を「ジャンプ」させることができます。
javaScriptではgoto文はないが、JavaSctiptでgotoのような振る舞いを模倣する
let step = 1;
loop: while(true) {
switch(step) {
case 1:
console.log('Step 1');
// 条件によって次のステップを変更
step = 2;
continue loop;
case 2:
console.log('Step 2');
// 条件によってループを終了または別のステップへ
break loop;
// 他のステップも同様に追加可能
}
}
飛び先が複数にならないように
7.7ネストを浅くする
SUCCCESSとSUCCCESSの否定系が混在しているのはタチが悪い
if (user_result === SUCCCESS) {
if(permission_result !== SUCCCESS){
reply.WriteErrors("error reading permissions");
reply.Done();
return;
}
reply.WriteErrors("");
}else{
reply.WriteErrors("user_result");
}
reply.Done();
早めに返してネストを削除する
if (user_result !== SUCCCESS) {
reply.WriteErrors("user_result");
reply.Done();
return
}
if(permission_result !== SUCCCESS){
reply.WriteErrors(perminssion_result);
reply.Done();
return;
}
reply.WriteErrors("");
reply.Done();
7.8実行の流れをおえるかい?
コードを「舞台裏」で実行する構成要素がある。
構成要素 | 高レベルの流れが不明瞭になる理由 |
---|---|
スレッド | どのコードがいつ実行されるのかよくわからない |
シグナル/割り込みハンドラ | 他のコードが実行される可能性がある |
例外 | いろんな関数呼び出しが終了しようとする |
関数ポインタと無名関数 | コンパイル時に判別できないのでどのコードが実行されるのかわからない |
8章巨大な式を分割する(99p~)
8.1説明変数
式を説明してくれるような変数にする方が良い
- if(line.spilit[0].step() === "root"){}
+ const userName = line.spilit[0].step()
+ if(userName === "root") {
+ }
8.2要約変数
管理や把握を簡単にする変数
if(request.user.id === document.owner_id){}
if(request.user.id !== document.owner_id){}
const userOwnsDocument && (request.user.id === document.owner_id)
if(userOwnsDocument){}
if(!userOwnsDocument){}
8.3ド・モルガンの法則を使う
- if(!(fileExists && !isProtected)){}
+ if(!fileExists || isProtected){}
8.4短絡評価の悪用
if(a || b)
- if((!(bucket = FindBucket(key))) || !bucket.IsOccupied());
+ const bucket = FindBucket(key)
+ if(bucket != NULL){
+ assert(!bucket.IsOccupied());
+ }
「頭がいい」コードに気をつける。あとで他の人がコードを読む時にわかりにくくなる
8.5例:複雑なロジックと格闘する
function complexLogic(x, y) {
// 条件式の例
return (x > 10 && y <= 20) || (x <= 5 && y >= 15) || (x === y);
}
function complexLogic(x, y) {
// 条件式の例
if (x > 10 && y <= 20) {
return true;
}
if (x <= 5 && y >= 15) {
return true;
}
if (x === y) {
return true;
}
return false;
}
8.6巨大な文を分割する
巨大な文は一箇所に全てを詰め込むのではなく分割して1つづつ理解できるようにした方が良い
const updateHighLight = (messageNum) => {
if(voteValue + messageNum === Up){
...
}
}
const thumbsUp = voteValue + messageNum === Up
const updateHighLight = (messageNum) => {
if(thumbsUp){
...
}
}
8.7式を簡潔にするもう1つの創造的な方法
function addStats(addFrom, addTo) {
addTo.totalMemory += addFrom.totalMemory;
addTo.freeMemory += addFrom.totalMemory;
addTo.swapMemory += addFrom.totalMemory;
}
このような時は分割代入で可読性を向上
function addStats(addFrom, addTo) {
// 分割代入を使用してオブジェクトのプロパティに直接アクセスし、加算する
const { totalMemory, freeMemory, swapMemory } = addFrom;
addTo.totalMemory += totalMemory;
addTo.freeMemory += freeMemory;
addTo.swapMemory += swapMemory;
}
9章 変数と読みやすさ(111p~)
9.1 変数を削除する
const reemoveOne = (array, valueToRemove){
- let indexToRemove = null;
for(var i = 0; i < array.length; i += 1){
if(array[i] === valueToRemove){
indexToRemove = i;
- break;
+ return;
}
}
- if(indexToRemove !== null){
- array.splice(indexToRemove, i)
- }
}
タスクはできるだけ早く完了する方がいい
9.2 変数のスコープを縮める
「プライベート」変数を作る
変数のことが見えるコード行数をできるだけ減らす
submitted = false //グローバル変数
let submitForm = function(formName) {
if(submitted){
return //二重投稿禁止
}
...
submitted = true
}
submitted = false //グローバル変数
let submitForm = (function(formName) {
submitted = false //以下の関数からしかアクセスされない
if(submitted){
return //二重投稿禁止
}
...
submitted = true
}())
グローバルスコープ
let f = function(){
for(i = 0; i < 10; i += i)...
}
変数iは意図せずに変数iを具orーバルスコープに入れている
letを使用してスコープを定義する
let x = 1
9.3 変数は一度だけ読み込む
変数が絶えず更新され続けると値を追跡する難易度があがる
const NUM_THRELDS = 10
9.4 最後の列
コードの再構成
10章 無関係の下位問題を抽出する
- 関数やコードブロックを見て「このコードの高レベルの目標は何か」と自問する
- 各行に対して「高レベルの目標に直接的に効果があるのか? あるいは無関係の下位問題を解決しているのか」を自問する
- 無関係の怪問題を解決しているコードが相当量あれば、それを別の関数にする
次はこちら