0

this.closureまたはしない限り、以下のコードが0から2までの数字を出力しない理由を誰かが説明できます new crazyClosure(i)crazyClosure.call({}, i)?

var crazyClosure = function (count) {
    var self = this;

    function closure () {
        console.log("closure", count);
    };

    this.closure = function () {
        console.log("this.closure", count);
    };

    console.log("init", count);
    setTimeout(function () {
        console.log('');
        console.log("call", count);
        closure();
        self.closure();
    }, 0);
}

for (var i = 0; i < 3; i++) {
    crazyClosure(i);
}

クロージャーが関数自体thisではなくアタッチされる場合のようです。crazyClosure

私は見ることを期待します:

init 0
init 1
init 2
call 0
closure 0
this.closure 0
call 1
closure 1
this.closure 1
call 2
closure 2
this.closure 2

しかし、私は得る

init 0
init 1
init 2
call 0
closure 0
this.closure 2
call 1
closure 1
this.closure 2
call 2
closure 2
this.closure 2

回答に従って別の例を追加します。この例では、呼び出すたびにオーバーライドされる 2 つの関数がthisあります。あなたの説明によると、私はまだそれがどのように起こるのか理解していません。windowcrazyClosureexpectedunexpected

function crazyClosure (count) {
    var self = this;

    this.expected = function () {
        console.log("this.expected", count);
        self.unexpected();
    };

    this.unexpected = function () {
        console.log("this.unexpected", count);
    };

    console.log("init", count);
    setTimeout(self.expected, 10);
}

for (var i = 0; i < 3; i++) {
    crazyClosure(i);
}

私は得る:

init 0
init 1
init 2
this.expected 0
this.unexpected 2
this.expected 1
this.unexpected 2
this.expected 2
this.unexpected 2

これは理解しなきゃ!!!ありがとう

4

2 に答える 2

3

クロージャーの概念を Javascript オブジェクトの概念から切り離すようにしてください。

それらはまったく無関係であり、学習中にそれらを混在させると、必要以上の混乱が生じる可能性があります.

あなたのコードでは、問題はコンストラクターのように見えますが、関数として呼び出されるため、作業crazyClosureするオブジェクトを受信せず、に設定されていると動作します。thisthiswindow

その結果、それselfも結び付けられwindow、呼び出しself.closure()はループが作成した最後のクロージャーを呼び出します。JavaScript はループが終了するまでタイムアウトを実行しないため、3 つのタイムアウト イベントはすべて最後のクロージャを呼び出します。

また、「Javascript のクロージャが壊れている」という態度からすぐに離れてください。他の人のコードが壊れていると最初に考えた場合、プログラミングを進めることはできません。それを個人的に受け取らないでください。また、質問をどれだけ丁寧に行っているかについての暴言だとは思わないでください。自分のコードは正しく、他人のコード (ライブラリ、コンパイラ、OS) は間違っていると考える精神的な習慣は、真の殺人者です... その罠にはまらないでください。

編集

2 番目の例はより複雑ですが、結果として得られる出力は、Javascript の実装が行うべきものです。

this前に述べたように、各呼び出しは、呼び出し時に演算子が使用されなかっwindowたためです。newcrazyClosure

2 つの「メソッド」expectedとは最終的にグローバル関数になり、実行後にoutside から直接unexpected呼び出すことができるため、これを確認できます。expected()crazyClosure

ただし、ローカル変数にアクセスできる新しいクロージャーでグローバルが上書きされるため、crazyClosure渡される値はsetTimeout毎回異なります。このグローバル変数は後で上書きされますが、値が既に に渡され、Javascript ランタイムがそれを保存して 10 ミリ秒後に呼び出すことができます。expectedcountexpectedsetTimeout

ただし、これら 3 つのクロージャーはすべてコード callself.unexpectedで呼び出され、これが発生すると、このグローバルは既に 3 回上書きされているため、「予期される」3 つの異なるクロージャーはすべて同じクロージャーを「予期しない」(最後のもの) と呼びます。それは現在 に保存されているものですwindow.unexpected

于 2013-10-03T05:57:38.740 に答える
1

を使用しない場合new、関数thisの先頭はcrazyClosure、新しいオブジェクトではなく、囲んでいるコンテキストです。したがって、すべての呼び出しは同じクロージャー関数の呼び出しを終了しますself(これはwindow、コードが埋め込まれていない場合です)。

への呼び出しごとに

this.closure = function () {
    console.log("this.closure", count);
};

と同等です

window.closure = function () {
    console.log("this.closure", count);
};

したがって、前の関数を置き換えます。最後の値を埋め込む関数は1 つだけ window.closureになります。これは、 で 3 回呼び出すこのユニークな関数ですsetTimeout

于 2013-10-03T05:39:26.700 に答える