在對一個MVC項目進行頁面修改時碰到一個Jquery裡面奇怪的事情。
在頁面中需要輸出很多Record Id,這些資料是固定長度的數字,為了美觀需要一排一排的輸出到頁面,這裡我把它定為每排十個。開始我覺得這是個非常之簡單的任務,立即就在輸出到頁面的語句前加了個IF語句對循環變量i進行判斷,如果i對10求餘等于0,那麼說明滿十了該加一個換行标簽</br>了(i是從0開始的)。
代碼如下:
出來的結果是這樣的:

沒有達到我們預期的效果,因為第一個資料時i等于0,0對10求餘是等于0的,也就是說第一個資料也滿足我們的if條件,是以在輸出時後面加了個換行标簽,但其實第一個資料是不應該加的。好吧,這是我算法的錯誤,再思考一下改了就是。
于是,我想都沒想又開寫了,再加一個對i=0時的判斷不就完事了嘛,修改後的代碼如下:
if(i%10==0&&i!=0) str += "</br>";
好,再運作,看結果:
結果還是有問題,于是我慢慢分析了一下,還是算法的問題。
第一個資料i=0時,确實沒有加</br>了,一切正常,問題出在第一排最後一個數,确定它的i=10, 10對10求餘等于0,于是該加</br>,是的,它确實加上了,但由于i是從0開始的,仔細分析一下,應該在i等于9,29,39….這樣的序号時才加</br>。于是思路出來了,我們在對i進行判斷時先讓i加上1再對10進行求餘,也就是(i+1)%==0,并且這時也不用考慮i=0的情況了,因為i=0時,i+1對10求餘不會等于0,第一個不會換行,這樣就會得到當i=9,29,39….這樣的序号時才加</br>。現在基本可以确定算法是沒有問題的了,如果執行正确的話肯定會得到我想要的結果。
其實分析到這裡,除了用(i+1)%==0外,還可以用i%10==9來作為判斷是否加</br>的依據,事後我也試過,用 i%10==9來進行判斷可以工作,沒有任何問題。如果我一開始就用這條語句,那就不會有下面這些問題了,但苦逼的是,我采用的是前者。于是麻煩來了。
我們先看這樣改了之後的結果。
代碼: if((i+1)%10) str += "</br>";
輸出:
根本一個</br>标簽都沒加進去!着實有點坑爹。我們再在浏覽器裡對js進行調試下看一看是什麼原因。結果發現if判斷語句對于每個i的取值判斷判斷結果都為假,是以if體内的語句一次都沒有被執行到。
首次進入循環,此刻i=0;
步進到下一條語句繼續執行,此刻的i為0,這個倒是可以為0的,因為i+1後并沒有賦給i,是以i為0。
繼續步進,這裡$('#recordIds').append(str);語句的append方法會轉到其他地方執行許多操作,但對我們這裡讨論的for循環無關,執行完其他操作後,再次回到for循環,進行第二次循環,這時i=1,
步進,然後情況和上面一樣了,在對if 條件語句判斷後結果為假,不執行if體内的語句,直接跳到$('#recordIds').append(str);
我懷疑在js裡面,(i+1)這樣的寫法主在if判斷語句裡有問題,它無法正确執行,之後試了很多形式進行判斷,隻要帶(i+1)的都不行。
難道是括号的問題?于是我把圖2時能夠執行的語句if(i%10==0&&i!=0) str += "</br>"; 改為if((i%10==0)&&(i!=0)) str += "</br>"; if((i%10)==0&&i!=0) str += "</br>";
這兩種形式進行嘗試,按理說括号根本不影響的,我們一樣會得到圖2中的結果。
好吧執行後的結果:
兩種方式都得到了跟圖2一樣的結果,說明加括号顯示地指定一下運算優先極是不會影響我們的程式正常執行的。那為什麼(i+1)不行。
好吧,既然(i+1)不行,那我就換一種表達式吧,幸好還有前導自增的形式讓i可以在進行運算前加1,于是我改成了如下的代碼:
if(++i%10==0) str += "</br>";
執行結果:
結果非常完美,正是我想要的。到此,我的需求解決了,但我還是沒搞懂if((i+1)%10==0)出了什麼問題。
另外就是,這裡雖然解決了需求,我還有個小小的擔心。因為上面使用的是++i,這個表達式是先将i自增1,自增後1後的i是儲存在了原來的i中的,也就是在執行if(++i%10==0) str += "</br>";中條件判斷後,i的值其實應該是自增後的值,是以這必然會影響到外面的for循環的循環次數。我們的for循環是用來周遊表的,把表中每個資料列印出來,正常情況下應該是從i=0到i=list.length.這麼多個數,由于我們在每次循環時都将i自增了1,可以預見,輸出結果最後将會比正常情況少一半。
帶着這樣的擔心,我将最最原始沒有改變時的程式運作,讓它輸出5個資料,
再在改後的程式中讓它也設定輸出5條資料:
一模一樣!并沒有像我想的那樣會有資料減少的情況!而且輸出完全一樣,沒有因為i自增而出現減少或者資料隔條輸出的現象。這說明for循環中的i并沒有受到影響,仍然是按照從0周遊到list對象最後條資料為止進行執行的。
但其實我們知道,在C#,C++或者其他進階語言中,在循環體中對循環變量進行了更改是會影響循環次數的,為此,我建了個C#的ConsoleApplication來展示。
代碼:
很明顯它會輸出0到4五個數,
現在在循環體内增加類似先前讨論中的if代碼,程式現在變為
這時輸出的是:
說明确實會影響循環次數。這是非常明顯的事情,但我還是實驗了一把。
是以,我隻能說jQuery裡面for周遊伺服器傳回的結果時,它的循環變量沒有受到循環體内的改變的影響。但循環體内的i使用的值卻是從目前循環變量那裡複制過來的。
1天後。。。
在公司浩哥帶領下,第一個問題原因找出來了。
要從最外層的for循環說起。我們知道C#中用foreach對一個字典進行周遊時,循環變量并不是單純的從0開始到所周遊對象長度結束為止的連續數字,而是對應着目前所周遊對象中的一條記錄。
我們再次用一個C#的ConsoleApplication來說明問題;
這裡定義一個Person類,再定義一個包含許多Person執行個體的清單,為了能夠用foreach周遊,這裡的清單我們用System.Collections.Generic 命名空間下的泛型清單List<>,因為它實作了IEnumerable
接口,可以用foreach進行周遊。
無需多說,當我們在調用i.Name時就已經很清楚此刻的i就是一個Person執行個體了。
同樣,在javascript中的for in 循環也是周遊對象或者數組的,通過chrome調試我清楚地看到了我遇到問題的代碼确實是周遊的一個數組;
數組裡一條完整的記錄包含key和value, key代表每條數組中每條記錄的鍵值,即0,1,2….value 表示了正在的資料值,即9117405….在上面的ConsoleApplication例子裡foreach循環中的i都代表了它周遊的對象,但javascipt裡有點不一樣,它隻表示key, 不包含值部分。是以我們不能調用i.value, 而隻能是調用list[i].toString()。但這跟if((i+1)%10==0)為什麼執行不成功有什麼關系呢,确實沒多大關系,這裡我隻是把i具體是什麼搞得更清楚了。
現在來看為什麼if((i+1)%10==0)沒執行成功。對這條語句設斷點在浏覽器裡調試。居然驚訝的發現,此刻i為0,但i+1=01!
坑爹出現在這裡,它會把i當成字元串然後和1相加最後得一個“01”字元串!我去。
是以這裡若想要得到i+1的數字結果的話,需要我們顯示地進行資料類型的轉換。改成如下代碼一切okay了。
if ((parseInt(i) + 1) % 10 == 0) str += "</br>";
總結:在思索與尋求答案的過程中更正了自己不少錯誤的認識,也學到了很多東西。任何一個小問題都可以觸類旁通産生與之關聯的更多問題,需要我們去發現去思索。寫出來和大家分享,希望能幫助到有類似情況的初學者。