簡體   English   中英

getJSON函數中覆蓋的變量值

[英]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,如果你想看看整個事情:

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

這是你的問題

您可能已經意識到for循環的運行速度有多快(而且速度非常快)但是與之相比,您的網絡速度並不快,這就是導致您出現問題的原因。讓我解釋一下

您正在使用帶有URL的$ .getJSON,其值取決於offlineName但您在成功回調中也使用了offlineName 。現在假設第一個請求。 offlineName是“ESL_SC2”現在ajax請求在URL中使用它,但通常由於網絡延遲,響應不會立即到達同時循環現在是第二次迭代。但是等待offlineName IS“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

看到這個你會發現它運行得很完美,但是當你注釋掉這個函數時(第39,53,54行),它又會恢復到舊的bug行為。你可能想知道一個簡單的函數如何能夠如此徹底地改變行為。這就是 - 它都是基於范圍鏈的

就像Java一樣,JS解釋器(以下稱為VM)現在逐行讀取代碼,當它達到hello的定義時,它只讀取它(研究參數,返回和內部代碼)然后繼續; 現在它已到達調用hello(URL,offlineName); 它在hello中運行代碼,但后來它意識到getJson有一個回調,此時無法調用它,所以它將它記錄在它的“稍后調用”列表中,同時記錄該函數使用的所有變量的值time [1]。即使在以后的循環迭代中, URLofflineName被重新初始化/分配了新值,它們也不會影響[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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM