2.

JavaScript のコメント|// と /* */・JSDoc・script タグ内の注意

編集
この記事の要点
  • コメントは //(1 行)と /* */(複数行)の 2 種類だけ
  • /* */ は入れ子にできない。中に */ があるとそこで閉じてしまう
  • HTML の中に書く場合、文字列の中の </script> がタグとして解釈される
  • /** */ の JSDoc を書くと、エディタが型を推論して補完してくれる
  • コメントは「なぜそうしたか」を書く。「何をしているか」はコードで表す

2 種類の書き方

// 1 行コメント

const price = 100; // 行末にも書ける(コードとの間は 2 スペース空ける)

/*
  複数行コメント
  何行でも書ける
*/

const total = /* 途中にも書ける */ price * 2;

PHP の # や Python の三重クォートに相当するものはありません。この 2 種類だけです。

入れ子にできない

/*
  const a = 1;
  /* 内側のコメント */     ← ここで閉じてしまう
  const b = 2;
*/                          ← 構文エラーになる

まとめてコメントアウトしたい範囲に /* */ が含まれていると壊れます。範囲のコメントアウトは // を各行に付けるのが安全です(エディタの Ctrl + / でまとめて付きます)。

正規表現の中の記号に注意

// 正規表現リテラルの中の */ でコメントが閉じることがある
/*
  const re = /a*\/b/;      ← この * / の並びで閉じてしまう
*/

// URL の // がコメント開始に見える場面もある
const url = "https://example.com";   // 文字列の中なので問題ない
// const url = https://example.com;  ← クォートを忘れると // 以降がコメントになる

HTML の中に書くとき

<script>
  // これは JavaScript のコメント

  const s = "</script>";     // ← ここでスクリプトが終わってしまう
</script>

HTML パーサーは文字列かコメントかを区別せず、最初に見つけた </script> でスクリプトを終わらせます。文字列に含める必要があるときは分割します。

const s = "</scr" + "ipt>";      // 分割する
const s = "<\/script>";           // バックスラッシュでエスケープする

そもそも外部ファイルに分けるのが確実です。HTML のコメント <!-- --> をスクリプトの中に書くのは、古いブラウザ向けの名残なので現在は不要です。

JSDoc で型を補う

/**
 * 税込価格を計算する。
 *
 * @param {number} price - 税抜価格
 * @param {number} [rate=0.1] - 税率
 * @returns {number} 税込価格(小数切り捨て)
 * @throws {RangeError} price が負のとき
 */
function taxIncluded(price, rate = 0.1) {
  if (price < 0) throw new RangeError("price は 0 以上");
  return Math.floor(price * (1 + rate));
}

/**(アスタリスク 2 個)で始めると、VS Code などが引数名と型を補完に出します。TypeScript を導入していなくても、型の恩恵をある程度受けられます。

/** @type {HTMLInputElement} */
const input = document.getElementById("name");
input.value = "田中";        // value の補完が効くようになる

/** @type {{ id: number, name: string }[]} */
const users = [];

// ファイル全体を型検査の対象にする
// @ts-check

ファイル先頭に // @ts-check を書くと、エディタが JSDoc をもとに型エラーを指摘してくれます。

特別な意味を持つコメント

コメント意味
// TODO: ...あとでやること。エディタが一覧表示できる
// FIXME: ...壊れていると分かっている箇所
// eslint-disable-next-line no-console次の 1 行だけ ESLint の指摘を無視
// prettier-ignore次の要素を整形しない(表形式の配列などで使う)
// @ts-ignore次の行の型エラーを無視(多用しない)
//# sourceMappingURL=...ソースマップの場所。ビルドツールが付ける
// prettier-ignore
const matrix = [
  1, 0, 0,
  0, 1, 0,
  0, 0, 1,
];

/* eslint-disable no-console */
console.log("この範囲は指摘されない");
/* eslint-enable no-console */

何を書くか

// 悪い例: コードを読めば分かることを繰り返している
// count に 1 を足す
count += 1;

// 悪い例: 変数名が説明を必要としている
// 経過日数
const d = 30;

// 良い例: 名前で説明する
const elapsedDays = 30;

// 良い例: なぜそうしたかを書く
// Safari 16 以前は scrollend が無いので setTimeout で代用する
setTimeout(onScrollEnd, 150);

// 良い例: 一見おかしく見える処理の理由
// 仕様上「0 件」と「未取得」を区別する必要があるため null で初期化する
let items = null;

コードは「何をしているか」を語れますが、「なぜそうしたか」は語れません。後から読む人が消したくなる処理ほど、理由をコメントに残す価値があります。

JSDoc で型を定義する

/**
 * @typedef {object} User
 * @property {number} id
 * @property {string} name
 * @property {string} [email]   省略可能
 */

/**
 * @param {User[]} users
 * @returns {string[]}
 */
function names(users) {
  return users.map((u) => u.name);
}

/** @type {User} */
const u = { id: 1, name: "田中" };

// 別ファイルの型を読み込む
/** @typedef {import("./types.js").User} User */

@typedef を書いておくと、そのファイル内で User を型名として使えます。TypeScript を導入せずに形の決まったデータを型で表せるのが利点です。

関数の説明に何を書くか

/**
 * 一覧を取得する。
 *
 * 失敗しても例外を投げず空配列を返す(画面を止めないため)。
 * 1 分間キャッシュするので、更新直後は古い値が返ることがある。
 *
 * @param {object} [options]
 * @param {number} [options.limit=20]
 * @returns {Promise<User[]>} 取得できなければ空配列
 */
async function fetchUsers(options = {}) { /* ... */ }

読む人が知りたいのは「呼んだあと何が保証されるか」です。失敗時の挙動、副作用の有無、キャッシュの有無を書くと、実装を読まずに使えるようになります。

コメントアウトしたコードを残さない

function total(items) {
  // return items.reduce((a, b) => a + b.price, 0);
  // return items.map((i) => i.price).reduce((a, b) => a + b);
  return items.reduce((a, b) => a + b.price * b.qty, 0);
}

古い実装が残っていると、どれが正しいのか読む人には判断できません。過去のコードは Git が覚えているので消してください。残す必要があるなら、なぜ残すのかをコメントに書きます。

本番でコメントは消える

npx esbuild app.js --minify --outfile=app.min.js

ビルドツールを通すとコメントは削除されるため、ファイルサイズを気にしてコメントを削る必要はありません。ただしビルドしていない素の JS はブラウザから丸見えです。API キーや内部の URL をコメントに書かないでください。

関連

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 記述方法
  2. コメント
  3. 変数の宣言
  4. 関数
  5. 演算子
  6. 条件文
  7. 配列
  8. 連想配列
  9. ループ処理
  10. 非同期処理
  11. 同期処理
  12. 確認ウィンドウを表示する方法
  13. 文字の置換
  14. base urlを取得する方法
  15. formのsubmit前にjavascriptを呼び出す方法
  16. undefinedのイコール判定
  17. Javascript のみで form を post で submit する方法