6

これは、ループ変数が別のスコープにないために、単純なJSループが期待どおりに動作しない状況の例です。

よく提示される解決策は、次のような不快に見えるループコードを作成することです。

for (var i in obj) {
    (function() {
        ... obj[i] ... 
        // this new shadowed i here is now no longer getting changed by for loop
    })(i);
}

私の質問は、これを改善できるかということです。これを使用できますか:

Object.prototype.each = function (f) {
    for (var i in this) {
        f(i,this[i]);
    }
};

// leading to this somewhat more straightforward invocation
obj.each(
    function(i,v) {
        ... v ...
        // alternatively, v is identical to
        ... obj[i] ...
    }
);

「スコープループ」が必要だと確認したときは?見た目はややすっきりしていて、通常のforループと同様のパフォーマンスが得られるはずです(同じように使用するため)。

更新:それはほとんどすべてObject.prototypeを壊すので、で物事を行うことは大きなノーノーのようです。

邪魔にならない実装は次のとおりです。

function each (obj,f) {
    for (var i in obj) {
        f(i,obj[i]);
    }
}

呼び出しは非常にわずかに変化します

each(obj,
    function(i,v) {
        ... v ...
    }
);

だから私は自分の質問に答えたと思います、jQueryがこのようにそれをするなら、本当に間違って行くことはできません。私が見落とした問題は、答えが必要です。

4

2 に答える 2

1

グローバルスコープ

何かがグローバルであるということは、コード内のどこからでもアクセスできることを意味します。たとえば、次のようにします。

var monkey = "ゴリラ";

function greetVisitor () {

return alert("Hello dear blog reader!");
}

そのコードが Web ブラウザーで実行されていた場合、関数のスコープはウィンドウになるため、

その Web ブラウザ ウィンドウで実行されているすべてのもので利用できます。

ローカル スコープ

グローバル スコープとは対照的に、ローカル スコープは、何かが定義されたばかりでアクセス可能な場合です。

関数など、コードの特定の部分。例えば;

関数 talkDirty () {

var saying = "Oh, you little VB lover, you";
return alert(saying);
}

alert(saying); // Throws an error

上記のコードを見ると、変数は talkDirty 内でのみ使用可能です。

関数。それ以外では、まったく定義されていません。注意事項:無言で宣言する場合

その前に var キーワードがあると、自動的にグローバル変数になります。

これが意味することは、ネストされた関数がある場合、内部関数がアクセスできるということです

関数変数と関数を含む:

function saveName (firstName) {

関数 capitalizeName () {

return firstName.toUpperCase();

} var capitalized = capitalizeName();

return capitalized;

}

alert(saveName("Robert")); // Returns "ROBERT"

先ほど見たように、内部関数 capitalizeName にはパラメータを送信する必要はありませんでしたが、完全な

外側の saveName 関数のパラメータ firstName へのアクセス。わかりやすくするために、別のものを見てみましょう

例:

関数兄弟 () {

var siblings = ["John", "Liza", "Peter"];

関数 siblingCount () {

var siblingsLength = siblings.length;

return siblingsLength;

}

関数 joinSiblingNames () {

return "I have " + siblingCount() + " siblings:\n\n" + siblings.join("\n");

}

return joinSiblingNames();

}

alert(siblings()); // Outputs "I have 3 siblings: John Liza Peter"

先ほど見たように、両方の内部関数は、包含関数の兄弟配列にアクセスできます。

各内部関数は、同じレベルの他の内部関数にアクセスできます (この場合、

joinSiblingNames は siblingCount にアクセスできます)。ただし、siblingCount の変数 siblingsLength は

その関数内、つまりそのスコープ内でのみ使用できます。

于 2013-01-01T11:25:33.590 に答える
1

each()あなたの答えはそれをほとんどカバーしていますが、何らかの理由で関数が便利でない場合に通常の for ループを使用することが合理的であるため、元のループの変更は注目に値すると思います。

更新:質問で参照されている例に似た例を使用して、さまざまなアプローチを比較するように変更されました。each()この関数では反復処理を行うためにデータが取り込まれた配列が必要なため、この例を調整する必要がありました。

次の設定を想定しています。

var vals = ['a', 'b', 'c', 'd'],
    max = vals.length,
    closures = [],
    i;

質問の例を使用すると、各反復中に 2 つの関数が作成されるため、元のループは最終的に 2n 関数 (n は反復回数) を作成します。

for (i = 0; i < max; i++) {
    closures[i] = (function(idx, val) {  // 1st - factoryFn - captures the values as arguments 
        return function() {              // 2nd - alertFn   - uses the arguments instead
            alert(idx + ' -> ' + val);   //                   of the variables
        };
    })(i, vals[i]);
}

これは、ループが開始される前にファクトリ関数を 1 回作成し、それを再利用することで、n + 1 個の関数のみを作成するように減らすことができます。

var factoryFn = function(idx, val) {
    return function() {
        alert(idx + ' -> ' + val);
    };
};

for (i = 0; i < max; i++) {
    closures[i] = factoryFn(i, vals[i]); 
}

これは、この状況で関数を使用する方法とほぼ同じeach()であり、合計で n + 1 個の関数が作成されます。ファクトリ関数は一度作成され、すぐに引数として に渡されeach()ます。

each(vals, function(idx, val) {
    closures[idx] = function() {
        alert(idx + ' -> ' + val);
    };
});

each()FWIW、使用する利点は、コードが少し短く、関数に渡されるときにファクトリ関数を正しく作成することで、each()これが唯一の用途であることを明確に示していると思います。ループ バージョン IMOの利点は、forループを実行するコードがすぐそこにあるため、その性質と動作が完全に透過的である一方で、each()関数が別のファイルで定義されたり、他の誰かによって記述されたりする可能性があることです。

于 2013-01-01T07:41:03.023 に答える