【问题标题】:Javascript: How to modify global variables within functions?Javascript:如何修改函数内的全局变量?
【发布时间】:2012-12-26 06:36:35
【问题描述】:

下面是我正在处理的代码。我想知道为什么当我在函数中更改变量ttt 时,更改不会留在函数之外?我在最顶部将其声明为var ttt = new Array;

还有,为什么我不能在函数中使用变量i

代码:

  client.on('connection', function()
    {
        var sss;
        var aaa;

            console.log('Connected');

        for (i = 0 ; i < 120 ; i++)
            ttt[i] = 0;

        for (i = 0 ; i < 9 ; i++)
        {
                client.getMatchHistory(434582, function(err, result)        
            {
                sss = JSON.stringify(result);
                var myObject = eval('(' + sss + ')');
                console.log (myObject.object.data[i].object.value);

                ttt[myObject.object.data[i].object.value]++;
            });

        }

            for (i = 0 ; i < 120 ; i++)
                console.log ("Record" + i + " Successes: " + ttt[i]);

    });

【问题讨论】:

  • 你能澄清一下你的问题吗?当您说“更改不会停留在功能之外”时,您是什么意思?另外,当您问“为什么我不能在函数中使用变量i?”时,您是什么意思?
  • ttt[myObject.object.data[i].object.value]++;这一行应该改变 ttt 数组。它确实 - 在函数中。但在函数之外,稍后:console.log("Record" + i + " Successes:" + ttt[i]);无论如何都只显示为0。至于我不工作......在函数中,console.log中的i(myObject.object.data[i].object.value);总是以未定义的形式返回并崩溃。如果我将 i 替换为一个数字,例如 0,它会起作用。为什么函数不能从 for (i = 0 ; i 中引用/识别 i

标签: javascript scope closures


【解决方案1】:

正如您所指出的,您的代码存在两个不同的问题,并且它们都有些相关。首先,ttt 正在全局修改。问题是您要在修改发生之前检查它们。我怀疑client.getMatchHistory() 正在进行异步调用。无法保证第二个 for 循环中的所有异步操作都会在您执行第三个 for 循环(您在其中读取数组)时执行完毕。

第二个问题是范围之一,但您的问题不是全局范围。由于client.getMatchHistory() 是异步的,一旦循环执行完毕,回调就会被调用。一旦循环完成执行i 将有10 的值。这可能不是您想要的。您需要创建一个回调生成 函数,它采用i 的值,并返回一个可用作回调的函数。像这样:

function make_callback(i) {
  return function(err, result) {
    // The body of your callback in here
  };
}

然后你应该在循环体中像这样使用它:

client.getMatchHistory(434582, make_callback(i))

这将在当前迭代中捕获i 的值,并且生成的回调将在执行时使用该值。这应该可以解决您使用i 的问题。

【讨论】:

    【解决方案2】:

    首先,所有全局变量实际上都是“窗口”对象字段,因此您可以使用 window.ttt 来确保您使用的是全局变量而不是本地变量。这段代码应该可以工作,那么你在开发者工具中试过了吗?调试器对此类变量的存在有何看法?

    至于变量 i:当然,你可以使用它,但最好在本地使用它,定义 'var i;'在函数的顶部不要破坏全局命名空间。

    【讨论】:

    • 并非所有有效的全局变量都是window 对象的属性。在所有其他脚本包含之前使用 var 声明的变量实际上是“全局的”(即:它们可以被随后声明/加载的所有代码访问和修改),但它们绝对不是 window 对象的属性。跨度>
    • @David-SkyMesh 不正确。即使你用var声明了一个全局变量,例如var my_global,你仍然可以通过window.my_global访问它。
    • @seliopou 不,你不能。我鼓励你先检查一下。
    • @seliopou 看来你是对的。我想我通过 MozRepl 的一些实验得到了错误的印象 - 某种抽象泄漏。
    • @David-SkyMesh 我怀疑是这样。 JavaScript 开发工具经常“破坏”作用域行为,通常是因为它们使用了 with 关键字,这几乎与 eval 一样糟糕,甚至更不被理解。没有伤害。
    【解决方案3】:

    client.getMatchHistory 可能是异步请求,您希望在循环之后,您将填充 ttt 数组,为了实现这一点,您必须创建一个在最后一个循环步骤之后运行的处理程序:

    var afterloop=function() {
        for (var i = 0 ; i < 120 ; i++)
           console.log ("Record" + i + " Successes: " + ttt[i]);
    }
    for (var i = 0 ; i < 120 ; i++)
            ttt[i] = 0;
    
    var i_final=0;
    for (var i = 0 ; i < 9 ; i++)
            {   
                var i=i;   //localise i
                client.getMatchHistory(434582, function(err, result)        
                {
                    i_final++;
                    sss = JSON.stringify(result);
                    var myObject = eval('(' + sss + ')');
                    console.log (myObject.object.data[i].object.value);
    
                    ttt[myObject.object.data[i].object.value]++;
                    if (i_final>8) {afterloop();}
                });
    
            }
    

    在示例中,i_final 计算完成的请求,它们可以按随机顺序完成,由于异步,因此在决定运行 afterloop() 时不能参考 i ,当 i_final 计数时超过最后一个执行的请求,你运行应该在最后一个请求完成后执行的函数。

    注意:请尽可能少使用全局变量,在您的代码中,您无缘无故地使用了全局 i

    【讨论】:

    • 窗口上的引用错误。我没有在浏览器中运行它,但是使用 Node.js... 有问题吗?
    • 那你为什么需要 gloval var 呢?在调用函数的地方添加var ttt=[];
    • 一些非常好的回应。谢谢。筛选一切。顺便说一句,eicto,你的解决方案奏效了,我只编写 JS 3 天,但这确实帮助我了解了更多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-08
    • 2020-04-04
    相关资源
    最近更新 更多