web-dev-qa-db-ja.com

可変変数はクロージャからアクセスできます。どうすれば修正できますか?

TwitterでTypeaheadを使用しています。私はIntellijからこの警告に遭遇しています。これにより、各リンクの「window.location.href」がアイテムのリストの最後のアイテムになります。

コードを修正するにはどうすればよいですか?

以下は私のコードです:

AutoSuggest.prototype.config = function () {
    var me = this;
    var comp, options;
    var gotoUrl = "/{0}/{1}";
    var imgurl = '<img src="/icon/{0}.gif"/>';
    var target;

    for (var i = 0; i < me.targets.length; i++) {
        target = me.targets[i];
        if ($("#" + target.inputId).length != 0) {
            options = {
                source: function (query, process) { // where to get the data
                    process(me.results);
                },

                // set max results to display
                items: 10,

                matcher: function (item) { // how to make sure the result select is correct/matching
                    // we check the query against the ticker then the company name
                    comp = me.map[item];
                    var symbol = comp.s.toLowerCase();
                    return (this.query.trim().toLowerCase() == symbol.substring(0, 1) ||
                        comp.c.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1);
                },

                highlighter: function (item) { // how to show the data
                    comp = me.map[item];
                    if (typeof comp === 'undefined') {
                        return "<span>No Match Found.</span>";
                    }

                    if (comp.t == 0) {
                        imgurl = comp.v;
                    } else if (comp.t == -1) {
                        imgurl = me.format(imgurl, "empty");
                    } else {
                        imgurl = me.format(imgurl, comp.t);
                    }

                    return "\n<span id='compVenue'>" + imgurl + "</span>" +
                        "\n<span id='compSymbol'><b>" + comp.s + "</b></span>" +
                        "\n<span id='compName'>" + comp.c + "</span>";
                },

                sorter: function (items) { // sort our results
                    if (items.length == 0) {
                        items.Push(Object());
                    }

                    return items;
                },
// the problem starts here when i start using target inside the functions
                updater: function (item) { // what to do when item is selected
                    comp = me.map[item];
                    if (typeof comp === 'undefined') {
                        return this.query;
                    }

                    window.location.href = me.format(gotoUrl, comp.s, target.destination);

                    return item;
                }
            };

            $("#" + target.inputId).typeahead(options);

            // lastly, set up the functions for the buttons
            $("#" + target.buttonId).click(function () {
                window.location.href = me.format(gotoUrl, $("#" + target.inputId).val(), target.destination);
            });
        }
    }
};

@cdhowieの助けを借りて、いくつかのコードを追加します。アップデータを更新し、click()のhrefも更新します

updater: (function (inner_target) { // what to do when item is selected
    return function (item) {
        comp = me.map[item];
        if (typeof comp === 'undefined') {
            return this.query;
        }

        window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
        return item;
}}(target))};
71

ここで2つの関数をネストし、(変数自体ではなく)変数の値をキャプチャする新しいクロージャーを作成する必要がありますクロージャーが作成された瞬間。これは、すぐに呼び出される外部関数の引数を使用して実行できます。次の式を置き換えます。

function (item) { // what to do when item is selected
    comp = me.map[item];
    if (typeof comp === 'undefined') {
        return this.query;
    }

    window.location.href = me.format(gotoUrl, comp.s, target.destination);

    return item;
}

これとともに:

(function (inner_target) {
    return function (item) { // what to do when item is selected
        comp = me.map[item];
        if (typeof comp === 'undefined') {
            return this.query;
        }

        window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);

        return item;
    }
}(target))

targetを外部関数に渡します。これは引数inner_targetになり、外部関数が呼び出されたときにtargetの値を効果的にキャプチャします。外部関数は、targetの代わりにinner_targetを使用する内部関数を返します。inner_targetは変更されません。

inner_targetの名前をtargetに変更でき、大丈夫です。最も近いtargetが使用されます。これは関数パラメーターになります。ただし、このような狭い範囲の同じ名前は非常に紛らわしいかもしれないので、私の例では異なる名前を付けました。

62
cdhowie

Closures Inside LoopsfromJavascript Garden

それを行う3つの方法を説明します。

ループ内でクロージャを使用する間違った方法

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);  
    }, 1000);
}

ソリューション1と匿名ラッパー

for(var i = 0; i < 10; i++) {
    (function(e) {
        setTimeout(function() {
            console.log(e);  
        }, 1000);
    })(i);
}

ソリューション2-クロージャから関数を返す

for(var i = 0; i < 10; i++) {
    setTimeout((function(e) {
        return function() {
            console.log(e);
        }
    })(i), 1000)
}

Solution 3、私のお気に入りで、bind-yaay!バインドFTW!

for(var i = 0; i < 10; i++) {
    setTimeout(console.log.bind(console, i), 1000);
}

Javascript garden をお勧めします-これは私にこれともっと多くのJavascriptの癖を見せてくれました(そして私はJSをもっと好きになりました)。

p.s。脳が溶けなかったら、その日は十分なJavascriptを持っていませんでした。

140

Ecmascript 6には、新しい機会があります。

letステートメントは、ブロックスコープのローカル変数を宣言し、オプションでそれを値に初期化します。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

10

JavaScriptのスコープはfunctionスコープのみであるため、単純にクロージャーを現在のスコープ外の外部関数に移動できます。

1
Oded Breiner