あなた、私の友人は完全に混乱しています。あなたの最初のステートメント自体が間違っています:
関数は、実行時ではなく定義時に環境 (スコープ) を作成します。
実際は逆です。関数を定義してもスコープは作成されません。関数を呼び出すと、スコープが作成されます。
スコープとは
簡単に言えば、スコープは変数の寿命です。ほら、すべての変数は生まれ、生き、そして死ぬ。スコープの開始は変数の発生時刻を示し、スコープの終了は変数の終了時刻を示します。
最初はスコープが 1 つしかありません (プログラム スコープまたはグローバル スコープと呼ばれます)。このスコープで作成された変数は、プログラムが終了したときにのみ消滅します。それらはグローバル変数と呼ばれます。
たとえば、次のプログラムを考えてみましょう。
const x = 10; // global variable x
{ // beginning of a scope
const x = 20; // local variable x
console.log(x); // 20
} // end of the scope
console.log(x); // 10
ここでは、 というグローバル変数を作成しましたx
。次に、ブロック スコープを作成しました。このブロック スコープ内で、ローカル変数を作成しましたx
。ローカル変数はログを記録するときにグローバル変数をシャドーx
するため、 が得られ20
ます。x
ログを取得すると、グローバルスコープに戻ります10
(ローカルx
は現在死んでいます)。
ブロック スコープと関数スコープ
現在、プログラミングには、ブロック スコープと関数スコープの 2 つの主なタイプのスコープがあります。
前の例のスコープはブロック スコープでした。それは単なるコードのブロックです。したがって、名前。ブロック スコープはすぐに実行されます。
一方、関数スコープはブロック スコープのテンプレートです。名前が示すように、関数スコープは関数に属します。ただし、より正確には、関数呼び出しに属します。関数スコープは、関数が呼び出されるまで存在しません。例えば:
const x = 10;
function inc(x) {
console.log(x + 1);
}
inc(3); // 4
console.log(x); // 10
inc(7); // 8
ご覧のとおり、関数を呼び出すたびに新しいスコープが作成されます。4
それが、出力、10
およびを取得する理由です8
。
もともと、JavaScript には関数スコープしかありませんでした。ブロックスコープはありませんでした。したがって、ブロックスコープを作成したい場合は、関数を作成してすぐに実行する必要がありました:
const x = 10; // global variable x
(function () { // beginning of a scope
const x = 20; // local variable x
console.log(x); // 20
}()); // end of the scope
console.log(x); // 10
このパターンは、即時呼び出し関数式 (IIFE)と呼ばれます。もちろん、最近では と を使用してブロック スコープの変数を作成できconst
ますlet
。
レキシカルスコープとダイナミックスコープ
関数スコープは、レキシカルとダイナミックの 2 つのタイプに分けることができます。ご覧のとおり、関数には 2 種類の変数があります。
- 自由変数
- バインドされた変数
スコープ内で宣言された変数は、そのスコープにバインドされます。スコープ内で宣言されていない変数は自由です。これらの自由変数は他のスコープに属していますが、どのスコープですか?
レキシカルスコープ
レキシカルスコープでは、自由変数は親スコープに属していなければなりません。例えば:
function add(x) { // template of a new scope, x is bound in this scope
return function (y) { // template of a new scope, x is free, y is bound
return x + y; // x resolves to the parent scope
};
}
const add10 = add(10); // create a new scope for x and return a function
console.log(add10(20)); // create a new scope for y and return x + y
JavaScript は、ほとんどのプログラミング言語と同様に、レキシカル スコープを持っています。
動的スコープ
レキシカル スコープとは対照的に、動的スコープでは、自由変数は呼び出しスコープ (呼び出し関数のスコープ) に属している必要があります。例(これもJSではありません-動的スコープはありません):
function add(y) { // template of a new scope, y is bound, x is free
return x + y; // x resolves to the calling scope
}
function add10(y) { // template of a new scope, bind y
var x = 10; // bind x
return add(y); // add x and y
}
print(add10(20)); // calling add10 creates a new scope (the calling scope)
// the x in add resolves to 10 because the x in add10 is 10
それでおしまい。シンプルですよね?
問題
最初のプログラムの問題は、JavaScript に動的スコープがないことです。レキシカルスコープしかありません。間違いがわかりますか?
function f1() {
var a = 1;
f2();
}
function f2() {
return a;
}
f1(); // a is not defined (obviously - f2 can't access the `a` inside f1)
あなたの 2 番目のプログラムは非常に混乱しています。
function f() {
var b = "barb";
return function() {
return b;
}
}
console.log(b); //ReferenceError: b is not defined
ここに間違いがあります:
- に電話したことはありません
f
。したがって、変数b
は作成されません。
f
変数を呼び出したとしても、b
ローカルになりf
ます。
これはあなたがする必要があることです:
function f() {
const b = "barb";
return function() {
return b;
}
}
const x = f();
console.log(x());
呼び出すx
と、 が返されますb
。b
しかし、それはグローバルにはなりません。グローバルにするには、これを行うb
必要があります。
function f() {
const b = "barb";
return function() {
return b;
}
}
const x = f();
const b = x();
console.log(b);
これがスコープと関数についての理解に役立つことを願っています。