名前付き関数式の隠された範囲


JavaScriptで関数を作成するにはいくつかの構文があります.一つは関数式と呼ばれます.
const f = function() {}
関数式の中に名前を追加することもできます.
const f = function internal(){}
関数がそのように作成された場合、変数internal 関数スコープ内で使用可能で、グローバルスコープでは使用できません.
const f = function internal(){
console.log(internal)
}
f(); // f internal(){...}
internal; // Reference error internal is not defined
クールクール、それは基本的であり、かなり簡単です.でも.
正確には変数internal 定義?

機能範囲
その変数を言いましょうinternal 関数スコープ内で定義されます.変数が関数スコープ内でのみアクセス可能であり、グローバルなものではアクセスできないことを確認したので、これはまともな推測です.しかし、定数と名前を作成する場合internal 関数本体の内部:
const f = function internal(){
const internal = 100;
console.log(internal)
}
f();
// 100 in console
コードはエラーを投げません、そして、我々がちょうど名前で定数を作成したようですinternal 変数を既に持っていたスコープでinternal (関数名から)しかし、問題はJSがコードの前にvar/let/const声明ですでに使われた識別子でconst声明を使用するのを許さないということです.それで、その問題を避ける2つの方法があります.
最初の方法は、関数式内に特殊なメカニズムがあると仮定することです.関数スコープは、関数式名(false)からインスタンス化された変数への作成とアクセスを制御します.
番目の-エレガントな何かを使用して、すでに存在します.

第二のアイデア-中間スコープ
実際にはJSの内部についての完全かつ詳細な情報を取得する唯一の方法です.それはECMAScript Language Specification . 確かにそれは簡単な読書ではなく、いくつかの経験が必要ですが、私を信じて、それはあなたの時間を投資する価値がある.
しかし、specの名前付き関数式の説明をチェックする前に、1つの有名なjs term - closureを更新しましょう
それで、閉鎖は機能がつくられる範囲での機能です.実際にはJSのすべての機能は閉鎖です.
const b = 20
const f = function (){
const a = 10;
a;
b;
}
f()
function f それは“学習”周囲のスコープが作成されます.時function f ローカル関数スコープを作成し、外側のスコープに、その作成中に記憶されている関数を呼び出します.

変数識別子解決はローカル関数スコープ(const a)から始まります.ローカルスコープ(const b)で変数が見つからない場合、外部スコープ(例のグローバルスコープ)に解像度を委譲します.それはスコープがお互いに連鎖している方法です.クール、簡単!
名前付き関数式に戻りましょう.があるsection 名前付き関数式の作成を記述する.
キーステップがあります.
FunctionExpression : function BindingIdentifier ( FormalParameters ) { FunctionBody }

2. Set name to StringValue of BindingIdentifier.
3. Let outerEnv be the running execution context's LexicalEnvironment.
4. Let funcEnv be NewDeclarativeEnvironment(outerEnv).
5. Perform funcEnv.CreateImmutableBinding(name, false).

8. Let closure be OrdinaryFunctionCreate(...).

11. Perform funcEnv.InitializeBinding(name, closure).
12. Return closure.
主なアイデアは、関数名を持つ1つだけの変数を維持するためのクロージャに追加の中間スコープを作成することです!
以下のコード例を示します.
const b = 20
const f = function internal(){
const a = 10;
a;
internal;
b;
}
f()
のように表示されます:

したがって、これは我々の最初の質問に対する答えですinternal 定義?"
変数internal グローバルスコープでは使用できず、関数スコープ内で同じ名前の変数の作成をブロックしませんinternal グローバルスコープと関数スコープの間のスコープ内に住んでいます.勝利!

最終部品
さて、我々はその変数internal 独自の範囲がありますが、定数または変数ですか?我々はそれに別の値を再割り当てすることがありますか?そのようにしましょう
const f = function internal(){
internal = 100;
console.log(internal)
}
f()
識別子internal 関数がまだ存在し、エラーが発生しません.これは実際面白いです、そして、私はそのような論理が全くユニークであると認めます.
仕様によればinternal この例ではCreateImmutableBinding抽象メソッドを使用して作成します.
このメソッドを使用して定数を作成しますが、2番目の引数としてブール値のフラグを持ちます.そのフラグがfalse その後、識別子に異なる値を割り当てることはできません.しかし、このような代入はエラーをスローしません.フラグがtrue を返します.