簡體   English   中英

可以從閉包訪問可變變量。 我怎樣才能解決這個問題?

[英]Mutable variable is accessible from closure. How can I fix this?

我在 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))};

我喜歡Javascript Garden 中的Closures Inside Loops段落

它解釋了三種方法。

在循環中使用閉包的錯誤方法

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)
}

解決方案 3 ,我最喜歡的,我想我終於明白了bind - yaay! 綁定FTW!

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

我強烈推薦Javascript 花園- 它向我展示了這一點以及更多 Javascript 怪癖(並使我更喜歡 JS)。

ps 如果你的大腦沒有融化,那你那天的 Javascript 還不夠。

你需要在這里築巢兩個功能,創建一個新的閉包捕獲變量(而不是變量本身)的此刻的值創建的閉包 您可以使用立即調用的外部函數的參數來執行此操作。 替換這個表達式:

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的值。 外部函數返回一個內部函數,它使用inner_target而不是target ,並且inner_target不會改變。

(請注意,您可以將inner_target重命名為target就可以了——將使用最接近的target ,這將是函數參數。但是,在如此緊湊的范圍內擁有兩個同名的變量可能會非常混亂,因此我在我的示例中對它們進行了不同的命名,以便您可以看到發生了什么。)

在 ecmascript 6 中,我們有了新的機會。

let語句聲明了一個塊作用域局部變量,可選擇將其初始化為一個值。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

由於 JavaScript 的唯一作用域是函數作用域,因此您可以簡單地將閉包移動到您所在作用域之外的外部函數。

只是為了澄清@BogdanRuzhitskiy 的回答(因為我不知道如何在評論中添加代碼),使用 let 的想法是在 for 塊中創建一個局部變量:

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

這幾乎適用於除 IE11 之外的所有現代瀏覽器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM