我正在应对一个挑战,必须显示脱机和在线的Twitch channel 。这是存在错误的函数:

function loadStreams(){
  for (var i = 0; i < channel_list.length; i++){
    offlineName = channel_list[i];
    console.log("offline name is: " + offlineName);
    URL = "https://wind-bow.hyperdev.space/twitch-api/streams/" + channel_list[i] + "?callback=?";
     $.getJSON(URL, function(data){
       console.log("Now offline name is: " + offlineName);
       console.log(data);
       if (data.stream !== null){
         currChannel = new Channel(data.stream.channel.display_name, data.stream.channel.status);
       }
       else {
         currChannel = new Channel(offlineName, "Offline");
       }
       outArr.push(currChannel);
    });
  }
  //showAll();
}

channe_list是一个预装有 channel 名称的字符串数组。定义如下:
var channel_list = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];

我的代码只是通过channel_list,获取JSON数据,然后返回结果并创建Channel对象的新实例,定义为:
var Channel = function(name, status){
  this.name = name;
  this.status = status;
}

由于某些原因,在我的else块中,变量“offlineName”总是被channel_list数组的最后一个值'noobs2ninjas'覆盖。换句话说,当我在else块中创建Channel类的实例时,“offlineName”始终为“noobs2ninjas”。请让我知道我在这里做错了。如果您想了解一下整个内容,这是我的CodePen:

https://codepen.io/tcao2/pen/XNbbbm?editors=1010

最佳答案

这是你的问题

您可能已经知道for循环运行的速度有多快(嗯,它非常快),但是与之相比您的网络根本不快,这就是导致您出现问题的原因。让我解释一下

您正在将$ .getJSON与URL结合使用,URL的值取决于offlineName,但您也在成功回调中也使用offlineNameofflineName是“ESL_SC2”,现在ajax请求在URL中使用它,但是通常情况下由于网络延迟,响应不会立即到达,同时循环正在进行第二次迭代。但是现在offlineName是“OgamingSC2”!并且将被使用,这样当您的第一个请求的成功回调完成但等待更多的条目时,即使是“OgamingSC2”也将在以后消失。此外,循环是如此之快,以至于第一个或第二个响应进入时,您的循环已经在其最后一次迭代中,因此只有最终的offlineName值(noobs2ninjas)得以保留,然后将其用于所有其他对象的成功回调中。

解决方案:解决方案是找到某种方式来保留每次迭代的offlineName值,并在相应的成功回调中使用该值。最简单的方法是使用let声明URLofflineName,从而限制了每次迭代的本质提供类似于闭合的效果

https://codepen.io/vsk/pen/LbNpBQ

上面代码的唯一问题是let是最近添加的,而较旧的浏览器并不很好地支持它,因此另一种解决方案是针对每个传递URLofflineName的请求实际实现关闭

(function(url,name) {
   $.getJSON(url, function(data){
   if (data.stream !== null){
     currChannel = new Channel(data.stream.channel.display_name, data.stream.channel.status);
   }
   else {
     currChannel = new Channel(name, "Offline");
   }
   outArr.push(currChannel);
  });
})(URL,offlineName);

https://codepen.io/vsk/pen/rWeOGL

编辑:这些被称为自执行函数,它们之间没有什么特别的地方,只是下面代码的简写形式
function hello(url,name){                //line #39
  //your code
}                                        //ln #53
hello(URL,offlineName);                  //ln #54

看到this,您会发现它运行完美,但是当您注释掉该函数(第39、53、54行)时,它又恢复为旧的错误行为。您可能想知道一个简单的函数将如何如此剧烈地改变行为这是方法-全部基于范围链

就像Java一样,JS解释器(以下称为VM)现在到达hello的定义时逐行读取您的代码,它只是读取它(研究参数,返回值和内部代码),然后继续;现在已经到达 call hello(URL,offlineName);它在hello中运行代码,但随后意识到getJson具有当前无法调用的回调,因此将其记录在“稍后将被调用”列表中,以及该函数中使用的所有变量的值,那时 [1]。因此,即使在以后的循环迭代中URLofflineName被重新初始化/分配了新值,它们也不会影响[1]中绑定(bind)的值,因为它们与它们无关,它们是完全不同的实体。这是因为JS按值传递参数(至少对于原始类型)

但是关于作用域链的最重要的事情是,即使循环超过了getJson回调中引用的值,仍然存在唯一的问题是您不能直接访问它们,但是VM可以访问。原因是-链中的最后一个功能是一个回调(记录在列表中),因此要想使VM在将来运行时必须让它生存所需的值, Nerd 称其为闭包,内部函数始终可以访问外部函数中存在的内容,甚至认为外部函数调用已结束,控制权又返回到其他地方。请注意,即使在您早期的错误代码值中都得到了保存,唯一的问题是它们被覆盖了,因为对于它们而言,它们所有人都只有一个外部函数,即loadStreams,但是当您创建并调用单独的hello时每个人都会创建一个单独的环境(类似于并行世界)。

本质上,它创建作用域链,因此每次迭代都可以拥有不受他人干扰的“自己的空间”。
for loop --> hello() --> getJson's inner function(每次迭代)

您可能会喜欢let,但首先请查看http://caniuse.com/#feat=let的兼容性表

关于javascript - getJSON函数中覆盖的变量值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40559324/

10-12 06:34