3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【2024年版】JavaScript(React)におけるリーダブルコードのチートシート7~9章

Last updated at Posted at 2024-02-28

2024/3/18
コードの再構成
10章 無関係の下位問題を抽出する

10.1 入門的な例: findClosestLocation()

こちらの記事の続編になります。

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のような振る舞いを模倣する

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の否定系が混在しているのはタチが悪い

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短絡評価の悪用

aがtrueならbは評価されない
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){
        ...
    }  
}
役割を分割させて可読性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章 無関係の下位問題を抽出する

  • 関数やコードブロックを見て「このコードの高レベルの目標は何か」と自問する
  • 各行に対して「高レベルの目標に直接的に効果があるのか? あるいは無関係の下位問題を解決しているのか」を自問する
  • 無関係の怪問題を解決しているコードが相当量あれば、それを別の関数にする

次はこちら

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?