【JavaScript】スタックトレースの読み方
/Users/mavo/project/algorithm-solutions/binary-tree/problems/10_inorderTraversal/js/src/inorderTraversal.js:10
arr.push(node.data);
^
TypeError: Cannot read properties of undefined (reading 'push')
at inorderTraversalHelper (/Users/mavo/project/algorithm-solutions/binary-tree/problems/10_inorderTraversal/js/src/inorderTraversal.js:10:13)
at inorderTraversalHelper (/Users/mavo/project/algorithm-solutions/binary-tree/problems/10_inorderTraversal/js/src/inorderTraversal.js:9:9)
at inorderTraversal (/Users/mavo/project/algorithm-solutions/binary-tree/problems/10_inorderTraversal/js/src/inorderTraversal.js:2:12)
at Object.<anonymous> (/Users/mavo/project/algorithm-solutions/binary-tree/problems/10_inorderTraversal/js/tests/inorderTraversalTest.js:6:13)
at Module._compile (node:internal/modules/cjs/loader:1369:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1427:10)
at Module.load (node:internal/modules/cjs/loader:1206:32)
at Module._load (node:internal/modules/cjs/loader:1022:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
at node:internal/main/run_main_module:28:49
スタックトレースの読み方の基本
エラーメッセージは上から下に「さかのぼる」形で表示されます。
つまり、一番上の行が「エラーが実際に起きた場所」で、
その下に行くほど「その関数を呼び出した関数」が順に書かれています。
→ スタックトレースでは、最初に呼び出された関数は一番下に表示されます。
エラーメッセージの内容
TypeError: Cannot read properties of undefined (reading 'push')
-
undefined なものに対して .push をしようとしてエラー
-
どこで? → 次の行がその答え
at inorderTraversalHelper (/.../inorderTraversal.js:10:13)
-
実際に .push を呼び出した行(=最初に壊れた場所)
-
/inorderTraversal.jsの 10行目、13文字目 -
この時点で result が undefined だったと思われる
at inorderTraversal (/.../inorderTraversal.js:2:12)
- この関数は再帰的に呼び出されていた
→ 9行目で inorderTraversalHelper() を呼んだ
at inorderTraversal (/.../inorderTraversal.js:2:12)
- inorderTraversal 関数から inorderTraversalHelper を呼び出した(多分 result を渡し忘れた)
at Object.<anonymous> (.../inorderTraversalTest.js:6:13)
-
テストファイルで inorderTraversal() を呼び出していた
-
テストの6行目から実行が始まった
時系列で見ると
-
テストファイル(inorderTraversalTest.js)の6行目で inorderTraversal() を呼び出した
-
その中で inorderTraversalHelper() を呼び出した(2行目)
-
さらにその中で inorderTraversalHelper() を再帰呼び出しした(9行目)
-
最終的に result.push(...) を実行しようとして result が undefined → エラー発生(10行目)
Node.jsが.jsファイルを実行するまで
at Module._compile (node:internal/modules/cjs/loader:1369:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1427:10)
at Module.load (node:internal/modules/cjs/loader:1206:32)
at Module._load (node:internal/modules/cjs/loader:1022:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
at node:internal/main/run_main_module:28:49
-
ユーザーが node yourScript.js を実行
-
Node.js がファイルを探す
-
ファイルを読み込んで、モジュールとして「コンパイル」する
-
モジュールとして実行する(require() や ES Modules にも対応)
at Module._compile (node:internal/modules/cjs/loader:1369:14)
ファイル内容(JavaScriptコード)を function にラップして実行する
Node.js は .js ファイルをそのまま実行せず、以下のように変換します
(function(exports, require, module, __filename, __dirname) {
// 自分のファイルのコードがここに来る
});
-
これにより、require や module.exports がローカルで使えるようになります
-
_compile() はこの「ラップ & 実行」を行なっている関数です
at Module._extensions..js (node:internal/modules/cjs/loader:1427:10)
.js ファイルをどう処理するか定義している関数
Node.js は拡張子ごとにロード方法を変える
-
.js→ JavaScriptとして読み込み -
.json→ JSONとして読み込み -
.node→ ネイティブ拡張として読み込み
_extensions['.js'] によって .js ファイルは _compile() に渡されます
at Module.load (node:internal/modules/cjs/loader:1206:32)
モジュールをロード(=読み込み準備)する
- モジュールがすでにキャッシュにあるかチェック
- なければファイルを読み込み _extensions に任せて処理
- exports を返す準備をする
at Module._load (node:internal/modules/cjs/loader:1022:12)
モジュールの実体を取得して返す
- キャッシュの再確認
-
Module.load()を呼ぶ - 最終的に
module.exportsを返す
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
ユーザーが指定した .js ファイルを実行するエントリーポイント
- ここが「Node.js の起動処理」→「ユーザーのコードを実行」への橋渡し
- 最初の .js ファイルを require() で呼び出す(CommonJS)
at node:internal/main/run_main_module:28:49
Node.js が node yourScript.js を処理しはじめる最初の地点
- コマンドライン引数からユーザーファイルを決定
-
executeUserEntryPoint()を呼んで起動
全体の流れ
$ node app.js
↓
run_main_module ← スクリプトの開始点
↓
executeUserEntryPoint ← ユーザーのファイルを起動
↓
Module._load ← モジュールを取得
↓
Module.load ← ロード処理
↓
Module._extensions.js ← .js ファイルとして扱う
↓
Module._compile ← 関数ラップして実行
↓
(あなたのコードが実行される)
Discussion