可变变量可以从闭包中访问。我怎样才能解决这个问题?

Posted

技术标签:

【中文标题】可变变量可以从闭包中访问。我怎样才能解决这个问题?【英文标题】:Mutable variable is accessible from closure. How can I fix this? 【发布时间】:2013-05-19 10:39:30 【问题描述】:

我正在使用 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));

【问题讨论】:

How to avoid access mutable variable from closure的可能重复 【参考方案1】:

您需要在这里嵌套两个函数,创建一个新的闭包,该闭包捕获变量的值(而不是变量本身)在创建闭包的那一刻。您可以使用立即调用的外部函数的参数来执行此操作。替换这个表达式:

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代替targetinner_target不会改变。

(请注意,您可以将inner_target 重命名为target,这样就可以了——将使用最接近的target,这将是函数参数。但是,在这样的情况下,有两个同名的变量一个狭窄的范围可能会让人很困惑,所以我在我的例子中给它们起了不同的名字,这样你就可以看到发生了什么。)

【讨论】:

第二部分呢?我在哪里设置按钮的href?这种做事方式对我来说似乎超级奇怪=/ @yaojiang 在那里应用相同的技术。如果您需要帮助,请告诉我,我们可以开始聊天。但首先尝试了解我的示例中发生了什么,然后尝试自己做。 (如果你自己做这个概念会更好!) 请务必注意,匿名函数通过引用围绕外部变量关闭。如果您想按值关闭它们,那么您需要引入一个具有您想要捕获的值并且不会更改的新变量——由于 javascript 没有块范围,因此最容易实​​现使用函数参数。 @yaojiang 这不是 JavaScript 独有的技术,而是广泛用于具有函数范围的函数式语言中。它看起来很奇怪,但也非常强大。给概念(尤其是闭包)一些时间来深入了解它,你将成为一个更好的程序员。 (直到我使用 JavaScript 进行了大量工作后,我才意识到 C# 匿名方法是闭包,当我回到 C# 时意识到我错过了一个非常强大的工具。) 这给了我学习更多高级 javascript 包括闭包的动力,感谢这个 sn-p!!!【参考方案2】:

我喜欢 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 - 耶!绑定FTW!

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

我强烈推荐 Javascript garden - 它向我展示了这一点以及更多 Javascript 怪癖(让我更加喜欢 JS)。

附言如果你的大脑没有融化,那你那天的 Javascript 就不够用了。

【讨论】:

【参考方案3】:

在 ecmascript 6 中,我们有了新的机会。

let 语句声明一个块范围的局部变量,可选择将其初始化为一个值。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

【讨论】:

【参考方案4】:

由于 JavaScript 的唯一作用域是 function 作用域,您可以简单地将闭包移动到您所在作用域之外的外部函数。

【讨论】:

【参考方案5】:

只是为了澄清@BogdanRuzhitskiy 的答案(因为我不知道如何在评论中添加代码),使用 let 的想法是在 for 块内创建一个局部变量:

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

这几乎适用于除 IE11 之外的任何现代浏览器。

【讨论】:

以上是关于可变变量可以从闭包中访问。我怎样才能解决这个问题?的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 从闭包内返回变量

JS 闭包

闭包中的Swift可变结构和结构的行为不同

从闭包中访问私有变量

关于闭包

彻底搞懂JavaScript中的作用域和闭包