g();
が評価される前にconst g = 【アロー関数】;
が評価されているのでエラーは起きません。重要なのはどの順番で評価されるかです。
関数宣言、関数式やアロー関数の定義自体が評価されるとき、関数オブジェクトは生成されますが、その関数の中身は全く評価されません。コード全体に対する文法チェックは行われますが、変数が存在するのか、メソッドが存在するのかと言ったことは一切チェックされません。ましてや、変数の初期化が終わっているのかなど全く見ることはありません。
それらの関数が呼び出されたとき、初めて関数の中身が評価されます。このときになって内部で宣言されていない変数をスコープチェーンを通じて探しに行き、どの変数であるかが決定されます。ちょうど、オブジェクトにおいてプロパティをプロトタイプチェーンで探しに行くことと同じです。
実際どうなるかを見ていきましょう。
- 始めに文法チェックが行われます。
- トップレベルで宣言の確認を行います。
- 変数
f
と変数g
がトップレベルのレキシカル環境レコードに登録されます。このとき、変数は__未初期化__(undefined
のことではありません)という特別な状態になります。
const f = 【アロー関数】;
の評価を始めます。
- まず、アロー関数の定義自体を評価し、書かれた中身の関数オブジェクトを生成します。このとき、関数の中身自体が評価されないことに注意してください。
- レキシカル環境レコードに登録済みの変数
f
が未初期化であることを確認し、先程生成した関数オブジェクトにf
に紐付けます。
const g = 【アロー関数】;
の評価を始めます。動作はf
と一緒で、生成された関数オブジェクトがg
に紐付くことになります。
f();
の評価を始めます。
f
を現在の環境(トップレベルのレキシカル環境)からスコープチェーン(トップレキシカル->グローバル)に従って検索します。トップレベルのレキシカル環境レコードにあるf
であることがわかります。
f
自体を評価し、f
に紐付けられた関数オブジェクトを得ます。
f
に紐付けられた関数オブジェクトに対して引数無しで呼び出し(call)を行います。
f
に紐付けられた関数オブジェクトの中身の評価を始めます。
console.log("call f");
を評価します。評価の結果、"call f"をコンソールに表示します。
g();
の評価を始めます。
g
を現在の環境(関数内部のレキシカル環境)からスコープチェーン(f内部レキシカル->f内部関数->トップレキシカル->グローバル)に従って検索します。トップレベルのレキシカル環境レコードにあるg
であることがわかります。
g
自体を評価し、g
に紐付けられた関数オブジェクトを得ます。
g
に紐付けられた関数オブジェクトに対して引数無しで呼び出し(call)を行います。
g
に紐付けられた関数オブジェクトの中身の評価を始めます。
console.log("call g");
を評価します。評価の結果、"call g"をコンソールに表示します。
- 関数の終端なので、戻り値
undefined
で戻ります。
- 関数の終端なので、戻り値
undefined
で戻ります。
- スクリプトの終端なので、処理が終了します。
変数が未初期化の状態で変数に対する評価を行うとReferenceError
が発生します。実際はもっと細かいエラー処理などを行っています。詳しくはECMAScriptの仕様書を確認してください。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/11/27 01:07