Javascript getJSON函数中覆盖的变量值
我正在进行一项挑战,我必须显示离线和在线的Twitch频道。下面是有bug的函数:Javascript getJSON函数中覆盖的变量值,javascript,Javascript,我正在进行一项挑战,我必须显示离线和在线的Twitch频道。下面是有bug的函数: 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/tw
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();
}
我的代码只需遍历channel_列表,获取JSON数据,返回结果并创建channel对象的新实例,定义如下:
var Channel = function(name, status){
this.name = name;
this.status = status;
}
由于某些原因,在my else块中,变量“offlineName”总是被通道列表数组的最后一个值“noobs2nijas”覆盖。换句话说,当我在else块中创建Channel类的实例时,“offlineName”总是“noobs2nijas”。请让我知道我做错了什么。这是我的密码笔,如果你想看看整个事情:
这是您的问题
您可能知道循环的运行速度有多快(很快),但与之相比,您的网络根本不快,这就是造成问题的原因。让我解释一下
您正在将$.getJSON与URL一起使用,URL的值取决于offlineName
,但您也在成功回调中使用offlineName
。现在假设为第一个请求offlineName
现在是“ESL_SC2”,ajax请求在URL中使用它,但像往常一样,由于网络延迟,响应不会立即到达,同时循环现在正在进行第二次迭代。但是等待offlineName
现在是“OgamingSC2”!!当您的第一个请求成功回调完成但等待时,将使用该命令,因为还有更多条目,所以即使是“OgamingSC2”也会在稍后被击败。此外,循环速度非常快,以至于在收到第一个或第二个响应时,您的循环已经处于最后一次迭代,因此只有最终的脱机名称
值(noobs2nijas)生存下来,然后用于所有其他函数的成功回调
解决方案:解决方案是找到某种方法,通过这种方法,每次迭代都会保留其离线名称
值,并在其相应的成功回调中使用相同的值。最简单的方法是使用let
声明URL
和离线名称
,这限制了它提供的每次迭代的范围类似于闭包的效果
上述代码的唯一问题是,let
是最近添加的,而较旧的浏览器不太支持它,因此另一个解决方案是实际实现每个传递URL
和offlineName的请求的闭包
(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);
编辑:这些函数称为自执行函数,它们没有什么特别之处,只是下面代码的简写版本
function hello(url,name){ //line #39
//your code
} //ln #53
hello(URL,offlineName); //ln #54
你会发现它运行得非常完美,但当你注释掉函数(第39、53、54行)时,它又回到了原来的错误行为。你可能想知道一个简单的函数怎么能如此彻底地改变行为。下面是如何-它都是基于作用域链的
就像Java一样,JS解释器(以下称为VM)现在一行一行地读取代码,当它到达hello
的定义时,它只是读取它(研究参数、返回和内部代码),然后继续;现在它已经到达调用hello(URL、offlineName);它在hello中运行代码,但随后它意识到,getJson
有一个回调,此时无法调用,因此它会将其记录在“以后调用”列表中,以及该函数当时使用的所有变量的值[1]。因此,即使在以后的循环迭代中,URL
和offlineName
重新初始化/分配了新值,它们也不会影响[1]中绑定的值,因为它们与它们没有关系,它们是完全不同的实体。这是因为JS按值传递参数(至少对于基本类型)
但关于作用域链最重要的一点是,即使在循环越过getJson
回调中引用的值之后,仍然存在唯一的问题是您不能直接访问它们,而VM可以。原因是-链中的最后一个函数是回调(记录在列表中)因此,为了让VM在将来运行时能够保留所需的值,书呆子们称之为闭包,在闭包中,内部函数将始终能够访问外部函数中存在的内容,即使外部函数调用已经结束,并且控制权已返回到其他地方。请注意,即使在您之前的错误代码中,值也会得到s唯一的问题是它们都被覆盖了,因为它们都只有一个外部函数,即loadStreams
,但当您创建并调用单独的hello
s时,每个函数都会创建一个单独的环境(类似于平行宇宙)
本质上,它创建了范围链,因此每个迭代都可以有自己的“空间”,在那里不受其他人的干扰
for循环-->hello()-->getJson的内部函数
(每次迭代)
你可能会喜欢让但是首先看一下兼容性图表,在非常感谢你的回复新手!有趣的是,问题在于网络的速度和循环运行的速度(我习惯了Java,刚刚开始学习js和web开发)。第一个解决方案对我来说很有意义(我将不得不研究“let”关键字),但是对于第二个解决方案,闭包做了什么?非常感谢!!
function hello(url,name){ //line #39
//your code
} //ln #53
hello(URL,offlineName); //ln #54