[英]Variable value overwritten in getJSON function
我正在開展一項挑戰,我必須顯示離線和在線的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/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是一個預先加載了通道名稱的字符串數組。 它的定義如下:
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,如果你想看看整個事情:
這是你的問題
您可能已經意識到for
循環的運行速度有多快(而且速度非常快)但是與之相比,您的網絡速度並不快,這就是導致您出現問題的原因。讓我解釋一下
您正在使用帶有URL的$ .getJSON,其值取決於offlineName
但您在成功回調中也使用了offlineName
。現在假設第一個請求。 offlineName
是“ESL_SC2”現在ajax請求在URL中使用它,但通常由於網絡延遲,響應不會立即到達同時循環現在是第二次迭代。但是等待offlineName
IS“OgamingSC2”現在!! 並且將在第一次請求的成功回調完成時使用,但等待有更多的條目,所以即使“OgamingSC2”也會在稍后被擊敗。此外,循環非常快,以至於在第一或第二響應進入時,你的循環已經處於最后一次迭代,因此只有最終的offlineName
值(noobs2ninjas)存活,然后用於所有其他的成功回調。
解決方案:解決方案是找到一些方法,每次迭代都會保留其offlineName
值,並在相應的成功回調中使用相同的方法。最簡單的方法是使用let
來聲明URL
和offlineName
,這限制了它本質上提供的每次迭代的范圍效果類似於封閉
https://codepen.io/vsk/pen/LbNpBQ
上面代碼的唯一問題是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);
https://codepen.io/vsk/pen/rWeOGL
編輯 :這些被稱為自動執行功能 ,他們沒有什么特別的,只是下面的代碼的簡寫版本
function hello(url,name){ //line #39
//your code
} //ln #53
hello(URL,offlineName); //ln #54
看到這個你會發現它運行得很完美,但是當你注釋掉這個函數時(第39,53,54行),它又會恢復到舊的bug行為。你可能想知道一個簡單的函數如何能夠如此徹底地改變行為。這就是 - 它都是基於范圍鏈的
就像Java一樣,JS解釋器(以下稱為VM)現在逐行讀取代碼,當它達到hello
的定義時,它只讀取它(研究參數,返回和內部代碼)然后繼續; 現在它已到達調用hello(URL,offlineName); 它在hello中運行代碼,但后來它意識到getJson
有一個回調,此時無法調用它,所以它將它記錄在它的“稍后調用”列表中,同時記錄該函數中使用的所有變量的值time [1]。即使在以后的循環迭代中, URL
和offlineName
被重新初始化/分配了新值,它們也不會影響[1]中綁定的值,因為它們與它們沒有關系,它們是完全不同的實體。這是因為JS按值傳遞參數(至少對於原始類型)
但是關於范圍鏈的最重要的事情是,即使在循環結束后, getJson
回調中引用的值仍然只有你不能直接訪問它們但VM可以。原因是 - 鏈中的最后一個函數是一個回調(記錄在列表中),所以為了使任何意義VM必須讓它在將來運行時能夠存活它所需的值,書呆子稱它為一個閉包,其中內部函數總是可以訪問外部函數中存在的東西,即使是外部函數函數調用已經結束,控件已經返回到其他地方。請注意,即使在你早期的錯誤代碼值得到保存,只有問題是它們被覆蓋,因為所有這些都只有一個外部函數,即loadStreams
但是當你創建並調用單獨的hello
每個人創建一個單獨的環境(類似於並行宇宙)。
從本質上講,它創建了范圍鏈,因此每次迭代都可以擁有它的“自有空間”,而不受其他人的干擾。
for loop --> hello() --> getJson's inner function
(每次迭代)
你也許會順利let
,但首先看看兼容性圖表在http://caniuse.com/#feat=let
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.