| 症状 | 原因 | 直し方 | 章 |
|---|---|---|---|
"1" + 2 が "12" になる | + は、どちらかが文字列なら連結する | 外から来た値は Number() で数値にしてから計算する | 04 |
結果が NaN になる | undefined や数値でない文字列を計算に使った | 入口で Number.isNaN を調べ、おかしければ throw する | 04 |
引数を渡し忘れても何も言われず NaN | 足りない引数は undefined になる | 既定値を書く ・ 中で確かめる | 06 |
| 違う値どうしが等しいと判定される | == が型を変換してから比べた | いつも === を使う | 04 |
0.1 + 0.2 が 0.3 にならない | 小数は 2 進数で近似される | お金は円単位の整数で計算する ・ 表示で toFixed | 03 |
7 / 2 が 3.5 になる | 数値は小数を含む 1 種類だけ | 整数にしたいときは Math.floor | 03 |
| 文字数が見た目より多い | length は UTF-16 の単位の数。絵文字は 2 | [...s].length | 03 |
| 症状 | 原因 | 直し方 | 章 |
|---|---|---|---|
TypeError: Cannot read properties of undefined (reading 'x') | 途中のプロパティが undefined だった | どこが undefined かを調べる。無いことがあるなら ?. | 03 |
if (count) で 0 が「無い」扱いになる | 0 ・ "" も falsy | count === undefined のように、何を調べたいかを書く | 04 |
既定値を入れたら 0 や "" が消えた | || は falsy をすべて置き換える | ?? を使う | 04 |
| 配列の範囲外を読んでもエラーにならない | 無い番号は undefined を返す | length で範囲を確かめる | 05 |
関数の結果が undefined | return を書き忘れた ・ アロー関数で { } を付けた | return を書く ・ 式 1 つなら { } を外す | 06 |
関数そのものが表示される([Function: f]) | () を付けずに関数を渡した | 呼ぶなら f() | 06 |
| 症状 | 原因 | 直し方 | 章 |
|---|---|---|---|
ReferenceError: x is not defined | 名前の書き間違い ・ 大文字と小文字の違い ・ スコープの外 | 綴りと、どの { } の中で作ったかを確かめる | 07 |
ReferenceError: Cannot access 'x' before initialization | let ・ const を、宣言の行より前で使った | 宣言を上に移す | 07 |
TypeError: Assignment to constant variable. | const の変数に入れ直した | 入れ直すなら let | 03 |
| 関数の中の変数が、外から見えてしまう | let ・ const を付け忘れ、グローバルにできた | 宣言を付ける。モジュールか strict モードにすると、エラーで気づける | 07 |
this.x が undefined ・ NaN | メソッドを取り出して(コールバックとして)呼んだ | () => obj.m() ・ obj.m.bind(obj) | 07 |
TypeError: Cannot read properties of undefined (reading 'x')(クラスの中で) | 同上。クラスは strict なので this が undefined | 同上 | 08 |
| ループで作った関数が、全部同じ値を見る | var のループ変数は 1 つしかない | let にする | 09 |
ReferenceError: Must call super constructor … | 子クラスの constructor で super() より前に this を使った | 先頭で super(…) を呼ぶ | 08 |
| 症状 | 原因 | 直し方 | 章 |
|---|---|---|---|
| コピーを変えたら、元も変わった | 代入は参照を写すだけ | { ...o } ・ [...arr] でコピーする | 05 |
| コピーしたのに、中の配列が元と一緒に変わる | ... のコピーは 1 段目だけ | structuredClone(o) | 05 |
同じ中身なのに === が false | オブジェクトの === は「同じものか」を比べる | 中身を 1 つずつ比べる | 05 |
[10, 9, 1].sort() が [1, 10, 9] | 引数なしの sort は文字列として並べる | sort((a, b) => a - b) | 05 |
| 並べ替えたら元の配列まで変わった | sort は元の配列を並べ替える | [...arr].sort(…) | 05 |
SyntaxError(JSON.parse で) | JSON の書式が正しくない。キーが " で囲まれていない、など | 中身を表示して形を確かめる。try ・ catch で受ける | 10 |
| 症状 | 原因 | 直し方 | 章 |
|---|---|---|---|
| 結果が出る前に次の行が動く ・ 表示の順番が違う | 非同期の処理を待っていない | await で待つ ・ 続きを then の中に書く | 11 |
| 値ではなく Promise が返ってくる | async 関数はいつも Promise を返す | 呼ぶ側で await する | 11 |
SyntaxError: await is only valid in async functions and the top level bodies of modules | async の付いていない関数の中で await を使った | 関数に async を付ける ・ 一番外側なら .mjs にする | 11 |
Uncaught (in promise) Error: … | 失敗した Promise を誰も受けていない | await して try ・ catch ・ .catch(…) を付ける | 11 |
try で囲んだのにエラーが捕まらない | エラーが起きたのは、try を抜けた後で動いた処理の中 | Promise にして await する | 11 |
| タイマーが時刻どおりに呼ばれない | 重い処理が流れを止めている | 重い処理を分ける ・ 短くする | 11 |
| 2 つの待ちに、合計の時間がかかる | await を 1 つずつ順に書いた | 関係が無ければ Promise.all | 11 |
| 症状 | 原因 | 直し方 | 章 |
|---|---|---|---|
ReferenceError: require is not defined in ES module scope, you can use import instead | ES Modules のファイルで require を使った | import に書き換える | 12 |
SyntaxError: Cannot use import statement outside a module | CommonJS として読まれるファイルで import を使った | 拡張子を .mjs にする ・ package.json に "type": "module" | 12 |
警告 MODULE_TYPELESS_PACKAGE_JSON | "type" の無い package.json の下で、.js に import を書いた | package.json に "type": "module" | 12 |
CommonJS から名前付きで import できない | Node.js が名前を見つけられない書き方だった | 既定の export として受けてから分ける | 12 |
あるはずのファイルが ENOENT で見つからない | パスはカレントフォルダから数えられる | join(import.meta.dirname, …) でスクリプトの場所から組み立てる | 12 |
| 読んだ行の末尾に見えない文字が付く | Windows の改行 \r\n の \r が残った | split(/\r?\n/) で分ける | 11 |
import で見つからない(拡張子なし) | ES Modules は拡張子を省略できない | "./math.mjs" と拡張子まで書く | 10 |
| 症状 | 原因 | 直し方 | 章 |
|---|---|---|---|
HTML をダブルクリックで開くと、スクリプトが動かない。Console に … has been blocked by CORS policy | file:// で開いたページからは、type="module" のファイルを読み込めない | ローカルサーバを立てて http://localhost:… で開く | 14 |
TypeError: Cannot read properties of null (reading 'textContent') | querySelector が要素を見つけられず null を返した。# ・ . の付け忘れ、綴りの違い | セレクタを確かめる | 15 |
| 入力した文字の中の HTML が、そのまま動いてしまう | 外から来た文字を innerHTML に入れた | textContent で入れる | 15 |
親で受けたクリックの e.target が、思った要素と違う | target は実際に押された、一番内側の要素 | e.target.closest("li") で目当ての要素までさかのぼる ・ 登録した要素なら e.currentTarget | 16 |
removeEventListener を呼んでも外れない | 登録したときと別の関数を渡した(その場で書いたアロー関数は、毎回別の関数) | 名前を付けた関数で登録し、同じ関数で外す | 16 |
クラスのメソッドをハンドラにしたら、this.count が読めない | addEventListener("click", this.up) と渡すと、this はボタンになる | () => this.up() ・ this.up.bind(this) で渡す | 16 |
| 送信ボタンを押すと、ページが読み込み直されて結果が消える | フォームの送信の既定の動き | submit イベントで e.preventDefault() | 17 |
数の欄の値を足すと、"12" のようにつながる | value は type="number" でもいつも文字列 | Number(input.value) にしてから計算する | 17 |
| 症状 | 原因 | 直し方 | 章 |
|---|---|---|---|
404 なのに catch に入らない | fetch は 404 ・ 500 でも成功として返る | res.ok を確かめて、だめなら自分で throw する | 18 |
TypeError: Failed to fetch | 通信そのものができなかった。ネットワークが切れている ・ CORS で止められた | Console の CORS のエラーを見る。別のオリジンなら、サーバ側で許可してもらう | 18 |
保存した回数に 1 を足すと "11" になる | localStorage は値を文字列にして保存する | 読むときに Number(…) | 19 |
保存したオブジェクトを読むと "[object Object]" | オブジェクトを、そのまま文字列にして保存した | JSON.stringify で保存し、JSON.parse で戻す | 19 |
| sessionStorage に入れた値が、別のタブで読めない | sessionStorage はタブごとに分かれ、タブを閉じると消える | タブをまたぐなら localStorage | 19 |