1

次のコードを検討してください。

function nepaliBuddha() {
    var a = 20;

    return function buddhaNepal() {
        console.log(a); 
    }
}

var closure = nepaliBuddha();

closure(); // logs 20
  1. 今、私たちが呼び出すときのclosure出力は20. これは、内部スコープ プロパティ ( [[scope]]) が、それが定義された内部関数に割り当てられたか、または宣言されたときに割り当てられたことを証明します。これが宣言時に割り当てられなかった場合、別のコンテキストで呼び出されるため、20 をログに記録する方法がありませんでした。

  2. 関数コンテキストclosure()のスコープ チェーンの呼び出しは、関数呼び出し時に作成され、現在のコンテキストのアクティブ化オブジェクトまたは VOと、この関数の内部[[scope]]プロパティで構成されます。

  3. 呼び出しも[[scope]]プロパティを作成します。これは、内部スコープのプロパティが宣言時と実行時に作成されることを意味しますね。

  4. 通常、定義では、[[scope]]実行時または関数呼び出し時にプロパティが作成されると記述されていますが、[[scope]]プロパティは宣言時に既に割り当てられているため、これは当てはまりません。

  5. [[scope]]関数の実行後にプロパティが更新される可能性があると思いますか?[[scope]] 内部プロパティの明確な定義を教えてください。宣言時、実行時、またはその両方で、いつどのように作成されるか。

4

2 に答える 2

9

うわー、あなたは完全に混乱していませんか。クロージャについてできるだけ簡単に説明します。

まず、スコープから始めます。スコープには次の 2 種類があります。

  1. ブロックスコープ
  2. 関数スコープ

ブロック スコープは、プログラム内で発生するとすぐに開始されます。一方、関数スコープは、関数が呼び出されるまで開始されません。したがって、同じ関数を複数回呼び出すと、複数のスコープが作成されます。

JavaScript にはブロック スコープがありません。関数スコープしかありません。したがって、ブロックスコープをエミュレートするには、関数式を作成してすぐに実行する必要があります。このパターンは即時呼び出し関数式 (IIFE)と呼ばれ、次のようになります。

(function () {
    // this is the JS equivalent of a block scope
}());

ブロック スコープと関数スコープの他に、スコープを分類する別の方法があります。したがって、次のものもあります。

  1. レキシカルスコープ
  2. 動的スコープ

ブロックスコープは常にレキシカルスコープであるため、この区別は関数スコープにのみ適用されます。JavaScript には字句スコープしかありません。

レキシカルスコープと動的スコープの違いを理解するには、自由変数と束縛変数の違いを理解する必要があります。

  1. 自由変数は、関数内で使用されるが、その関数内で宣言されていない変数です。
  2. 関数内で宣言された変数は、その関数にバインドされていると言われます。

次のプログラムを検討してください。

function add(x, y) {
    return x + y; // x and y are bound to add
}

上記のプログラムでは、変数xyは 内で宣言されているため、関数 add にバインドされていますadd

一方、次のプログラムの変数xとは、関数内で宣言されていませんが、関数内で使用されているため、関数内では自由です。yaddaddadd

function add() {
    return x + y; // x and y are free within add
}

今、自由変数が問題です。それらは何らかの値にマップする必要がありますが、どの値ですか? ここでレキシカル スコープとダイナミック スコープの出番です。主要な詳細については説明しませんが、ウィキペディアで読むことができます。

スコープは、プロトタイプの継承によく似ています。新しいスコープが始まると、JavaScript のプロトタイプ チェーンのように、スコープのチェーンを形成する親スコープから継承されます。

レキシカルスコープと動的スコープは、新しいスコープがフォームを継承する親スコープに関して異なります。

  1. レキシカルスコープでは、新しい関数スコープは、その関数が定義されたスコープ (つまり、そのレキシカル環境) から継承します。
  2. 動的スコープでは、新しい関数スコープは、その関数が呼び出されたスコープ (つまり、呼び出しスコープ) から継承します。

JavaScript にはレキシカル スコープしかないので、動的スコープは気にしません。次のプログラムを検討してください。

var count = 0;

function incrementCount() {
    return ++count;
}

(function () {
    var count = 100;
    alert(incrementCount()); // 1
}());

ここで、関数incrementCounterには 1 つの自由変数 - がありcountます。JavaScript にはレキシカル スコープがあるため、IIFE 内で宣言されたローカル変数ではなくcount、グローバル変数にマップされます。したがって、 を返し、 を返しません。countcountincrementCount1101

クロージャーは、レキシカル スコープを持つ言語でのみ機能するようになりました。次のプログラムを検討してください。

function getCounter() {
    var count = 0;

    return function () {
        return ++count;
    };
}

var counter = getCounter();

alert(counter()); // 1
alert(counter()); // 2
alert(counter()); // 3

上記のプログラムでは、によって返される関数は、次の理由getCounterにより、変数に関するクロージャです。count

  1. 変数countは、返された関数 (つまりcounter) 内で解放されます。
  2. 関数は、count宣言されているスコープの外に移動されます。

関数がクロージャーと呼ばれるには、これらの両方の条件が必要です。詳細については、次の回答をお読みください: https://stackoverflow.com/a/12931785/783743

ここで理解しておくべき重要なことは、関数counterが呼び出されなくても、クロージャーとして呼び出されるということです。クロージャは、変数 (クロージャの上位値と呼ばれます)を閉じる関数です。

呼び出すときにgetCounter新しいスコープを作成し (このスコープを呼び出しましょう)、 (つまり)Aによって返される関数を呼び出すたびに、scope から継承する新しいスコープを作成します。それで全部です。新しい閉鎖は作成されません。getCountercounterA

于 2013-07-08T06:39:55.503 に答える