4

JavaScript では、クロージャーは、それを含む関数の変数にアクセスできるネストされた関数として定義できることを知っています。例えば:

function outerFunction(x, y) {
    function innerFunction() {
        return x + y + 10;
    }
    return innerFunction;
}

onreadystatechange次のコードは、リクエスト オブジェクトのプロパティのコールバックを接続しています。ただし、定義により、これもクロージャーと見なされるかどうか疑問に思っていました

/* This is a contrived example, I know. 
 * Bear with me - it demonstrates the point I'm trying to convey. */

function submitHandler() {

    var oRequest = createRequest(); // assume I'm getting an instance of the xhr
    var sUsername = 'Tom';          // assume this is needed for work in the handler
    var This = this;
    oRequest.onreadystatechange = function() {
        This.handleResponse(oRequest, sUsername)
    }

}

function handleResponse(oResponse, sUsername) {
    if(oResponse.readyState === 4 && oResponse.status === 200) {
        // do work with the username
    } else {
        // we're not done yet...
    }
}

このhandleResponse関数は のコンテキストで無名関数として記述することもできsubmitHandlerますが、コールバックをコールバックする関数のスコープ外でコールバックを定義すると、より複雑な Ajax コードをより読みやすく、簡単に保守できることがわかりました。繰り返しますが、これは私の質問の要点を単に示すことを期待して私が使用している不自然な例です。

4

3 に答える 3

3

はい、それが定義による閉鎖であると仮定するのは正しいです。

あなたは自分のことを知っているように聞こえますが、ここには javascript クロージャーに関する優れた広範な記事があります。

于 2009-01-19T13:19:59.100 に答える
3

インラインクロージャー関数が多すぎると読みやすさが向上しないことに心から同意します。一方、宣言が冗長すぎるためvar self = this、クロージャーの記述スタイルは非常に嫌いです。これthisは単なる変形であり、独自の新しい「キーワード」を導入しthisますself

あなたが望むのは、メソッドをカリー化/バインドすることです。

function $A(o)
{
    var a = [];
    for (var i = 0; i < o.length; ++i)
        a.push(o[i]);
    return a;
}

Function.prototype.bind = function()
{
   var _method = this;
   var a = $A(arguments)
   var o = a.shift();

   return function()
   {
       return _method.apply(o, a.concat($A(arguments)));
   }
}

Function.prototype.curry = function()
{
   var _method = this;
   var a = $A(arguments);

   return function()
   {
       return _method.apply(null, a.concat($A(arguments)));
   }
}

メソッドはPrototypeライブラリから取得されます。これらは非常に便利なので、Prototype ライブラリを使用しないプロジェクトでも使用します。

あなたの場合、これは次のように書く代わりに意味します:

var This = this;
oRequest.onreadystatechange = function() {
    This.handleResponse(oRequest, sUsername)
}

あなたは今書くことができます:

oRequest.onreadystatechange = this.handleResponse.curry(oRequest,sUsername);

ただし、 this キーワードの意味を転送したい場合は、これを行うことができます

oRequest.onreadystatechange = this.handleResponse.bind(this,oRequest,sUsername);

handleResponseは、呼び出されると、 と同じthisコンテキストを持ちますsubmitHandler

于 2009-01-19T13:25:14.843 に答える
1

最初の例は閉鎖ではありません。innerFunction を返せばよかったのですが、innerFunction を実行して結果を返しています。

2 番目の例は機能し、クロージャを使用しています。この特定の手法に関する 1 つの注意点は、一部のブラウザー実装 (IE はもちろん、他の実装である可能性もあります) で非常に深刻なメモリ リークが発生する可能性があることです。oRequest に保持されている xhr と、関数を参照する onreadystatechange と、xhr が保持されているスコープであるその関数スコープとの間に循環参照が作成されます。xhr がメモリ内に残るだけでなく、応答が大きい場合 (特に XML の場合) はすぐにメモリを食い尽くす可能性があります。

応答の処理中に onreadystatechange プロパティを空のグローバル レベル関数に設定します。これは円を壊します。

于 2009-01-19T13:37:12.620 に答える