Clean JavaScript


Writing Clean JavaScript -  ES6 Edition

識別しやすいコード


仕事をするとき、どのような基準を立てて仕事をするかは、いつも混同されます.
私から見れば、コードを見ている人とコードを書いている人との会話のように、自然に読みやすいコードで仕事をしたいと思っていますが、いつも真ん中に行くと、最初から守っていた原則も守らないこともあり、コードを書くのを急いでいるために動作だけをするコードに書かれることもあります.
そこで、最も基本的な考え方を守るために、変数名、関数、エラー処理、分岐処理などを整理したブログがあり、簡単に整理しました.

1.変数


周囲で作業する場合、「a」や「first」などの意味のない変数名でコードを記述することがよく見られます.
ワークロードがますます大きくなり、プロジェクトがますます大きくなると、意味のない変数名で書かれたコードは、エラーや後続の再構築でワークロードが大きくなり、作業中に計画や設計に修正が必要でコードを修正する必要がある場合、何度も気まずい状況に遭遇します.
意味のある名前を使う
本人は一般的に関数名を書くとき、動詞を前に、名詞を後ろに、一般変数と区別します.
const foo = 'name' // X

const userName = 'name'
不要なコンテキストの追加を禁止
const user = {
	userId: 'abcd'
} // X

const user = {
	id: 'abcd'
}
ハードコーディング禁止値
setTimeout(clearSessionData, 900000); // X

const SESSION_DURATION_MS = 15 * 60 * 1000
setTimeout(clearSessionData, SESSION_DURATION_MS);

2.関数


記述名
function toggle(){...
} // X

function toggleThemeSwitcher(){...
}
デフォルトのパラメータの使用
function printAllFilesDirectory(dir){
	const directory = dir || "./";
} // X

function printAllFilesDirectory(dir = "./"){
}
パラメータ数の制限
function sendPushNotification(title, message, image, isSilent){
} // X

function sendPushNotification({title, message, image, isSilent}){
}
1つの関数の複数のアクション
function pingUsers(users) {
  users.forEach((user) => {
    const userRecord = database.lookup(user);
    if (!userRecord.isActive()) {
      ping(user);
    }
  });
} // X

function pingInactiveUsers(users) {
  users.filter(!isUserActive).forEach(ping);
}

function isUserActive(user) {
  const userRecord = database.lookup(user);
  return userRecord.isActive();
}
条件文の回避
function createFile(name, isPublic) {
  if (isPublic) {
    fs.create(`./public/${name}`);
  } else {
    fs.create(name);
  }
} // X

function createFile(name) {
  fs.create(name);
}

function createPublicFile(name) {
  createFile(`./public/${name}`);
}
自分で缲り返さないで
  • 独自のコードの再利用を禁止する
  • function renderCarsList(cars) {
      cars.forEach((car) => {
        const price = car.getPrice();
        const make = car.getMake();
        const brand = car.getBrand();
        const nbOfDoors = car.getNbOfDoors();
    
        render({ price, make, brand, nbOfDoors });
      });
    }
    
    function renderMotorcyclesList(motorcycles) {
      motorcycles.forEach((motorcycle) => {
        const price = motorcycle.getPrice();
        const make = motorcycle.getMake();
        const brand = motorcycle.getBrand();
        const seatHeight = motorcycle.getSeatHeight();
    
        render({ price, make, brand, seatHeight });
      });
    } // X
    
    function renderVehiclesList(vehicles) {
      vehicles.forEach((vehicle) => {
        const price = vehicle.getPrice();
        const make = vehicle.getMake();
        const brand = vehicle.getBrand();
    
        const data = { price, make, brand };
    
        switch (vehicle.type) {
          case "car":
            data.nbOfDoors = vehicle.getNbOfDoors();
            break;
          case "motorcycle":
            data.seatHeight = vehicle.getSeatHeight();
            break;
        }
    
        render(data);
      });
    }
    側面異常を避ける
  • JavaScriptコマンドベースの関数
  • を使用
  • 関数を純
  • に保持する
  • すべての側面効果を集中化します.
  • let date = "21-8-2021";
    
    function splitIntoDayMonthYear() {
      date = date.split("-");
    }
    
    splitIntoDayMonthYear();
    
    // Another function could be expecting date as a string
    console.log(date); // ['21', '8', '2021'];
    // X
    
    function splitIntoDayMonthYear(date) {
      return date.split("-");
    }
    
    const date = "21-8-2021";
    const newDate = splitIntoDayMonthYear(date);
    
    // Original vlaue is intact
    console.log(date); // '21-8-2021';
    console.log(newDate); // ['21', '8', '2021'];
    function enrollStudentInCourse(course, student) {
      course.push({ student, enrollmentDate: Date.now() });
    } //  X
    
    function enrollStudentInCourse(course, student) {
      return [...course, { student, enrollmentDate: Date.now() }];
    }

    3.ステータス


    if-else、switchなどの条件文は使用しませんが、まだ慣れていないので、条件文で書くことが多いです.
  • 不必要な否定文の使用を禁止する
  • .
    function isUserNotVerified(user) {
      // ...
    }
    
    if (!isUserNotVerified(user)) {
      // ...
    }
    // X
    
    function isUserVerified(user) {
      // ...
    }
    
    if (isUserVerified(user)) {
      // ...
    }
  • 速記
  • を使用
    if (isActive === true) {
      // ...
    }
    
    if (firstName !== "" && firstName !== null && firstName !== undefined) {
      // ...
    }
    
    const isUserEligible = user.isVerified() && user.didSubscribe() ? true : false;
    
    //////////////////////////// X
    
    if (isActive) {
      // ...
    }
    
    if (!!firstName) {
      // ...
    }
    
    const isUserEligible = user.isVerified() && user.didSubscribe();
  • ナスと直接車に戻るのを避ける
  • function addUserService(db, user) {
      if (!db) {
        if (!db.isConnected()) {
          if (!user) {
            return db.insert("users", user);
          } else {
            throw new Error("No user");
          }
        } else {
          throw new Error("No database connection");
        }
      } else {
        throw new Error("No database");
      }
    }
    // ------------------------------X---------------------
    
    function addUserService(db, user) {
      if (!db) throw new Error("No database");
      if (!db.isConnected()) throw new Error("No database connection");
      if (!user) throw new Error("No user");
    
      return db.insert("users", user);
    }
  • スイッチ
  • の代わりにオブジェクトを使用
    const getColorByStatus = (status) => {
      switch (status) {
        case "success":
          return "green";
        case "failure":
          return "red";
        case "warning":
          return "yellow";
        case "loading":
        default:
          return "blue";
      }
    };
    // ------------------------X------------------------
    
    const statusColors = {
      success: "green",
      failure: "red",
      warning: "yellow",
      loading: "blue",
    };
    
    const getColorByStatus = (status) => statusColors[status] || "blue";
  • 番人チェニン、Null連結演算子
  • を使用
    const user = {
      email: "[email protected]",
      billing: {
        iban: "...",
        swift: "...",
        address: {
          street: "Some Street Name",
          state: "CA",
        },
      },
    };
    
    const email = (user && user.email) || "N/A";
    const street =
      (user &&
        user.billing &&
        user.billing.address &&
        user.billing.address.street) ||
      "N/A";
    const state =
      (user &&
        user.billing &&
        user.billing.address &&
        user.billing.address.state) ||
      "N/A";
    // ---------------------X----------------------
    
    const email = user?.email ?? "N/A";
    const street = user?.billing?.address?.street ?? "N/A";
    const state = user?.billing?.address?.state ?? "N/A";

    4.同時性

  • ダイヤルバックの回避-await/async
  • 5.エラー処理

    // Don't ❌
    try {
      // Possible erronous code
    } catch (e) {
      console.log(e);
    }
    
    // Do ✅
    try {
      // Possible erronous code
    } catch (e) {
      // Follow the most applicable (or all):
      // 1- More suitable than console.log
      console.error(e);
    
      // 2- Notify user if applicable
      alertUserOfError(e);
    
      // 3- Report to server
      reportErrorToServer(e);
    
      // 4- Use a custom error handler
      throw new CustomError(e);
    }

    6.コメント

  • ビジネスロジック注釈
  • のみ
    // Don't ❌
    function generateHash(str) {
      // Hash variable
      let hash = 0;
    
      // Get the length of the string
      let length = str.length;
    
      // If the string is empty return
      if (!length) {
        return hash;
      }
    
      // Loop through every character in the string
      for (let i = 0; i < length; i++) {
        // Get character code.
        const char = str.charCodeAt(i);
    
        // Make the hash
        hash = (hash << 5) - hash + char;
    
        // Convert to 32-bit integer
        hash &= hash;
      }
    }
    
    // Do ✅
    function generateHash(str) {
      let hash = 0;
      let length = str.length;
      if (!length) {
        return hash;
      }
    
      for (let i = 0; i < length; i++) {
        const char = str.charCodeAt(i);
        hash = (hash << 5) - hash + char;
        hash = hash & hash; // Convert to 32bit integer
      }
      return hash;
    }
  • バージョンのコントロール
  • を使用
    // Don't ❌
    /**
     * 2021-7-21: Fixed corner case
     * 2021-7-15: Improved performance
     * 2021-7-10: Handled mutliple user types
     */
    function generateCanonicalLink(user) {
      // const session = getUserSession(user)
      const session = user.getSession();
      // ...
    }
    
    // Do ✅
    function generateCanonicalLink(user) {
      const session = user.getSession();
      // ...
    }
  • リスト
  • を記入する