在對一個MVC項目進行頁面修改時碰到一個Jquery裡面奇怪的事情。
在頁面中需要輸出很多Record Id,這些資料是固定長度的數字,為了美觀需要一排一排的輸出到頁面,這裡我把它定為每排十個。開始我覺得這是個非常之簡單的任務,立即就在輸出到頁面的語句前加了個IF語句對迴圈變數i進行判斷,如果i對10求餘等於0,那麼說明滿十了該加一個換列標籤</br>了(i是從0開始的)。
代碼如下:
for (var i in list) { var str="<span>"+list[i].toString()+"</span>    "; if(i%10==0) str += "</br>"; $('#recordIds').append(str);//此語句將一條資料輸出到頁面 }
出來的結果是這樣的:
沒有達到我們預期的效果,因為第一個資料時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來展示。
代碼:
namespace ConsoleApplication5{ class Program { static void Main(string[] args) { for (int i = 0; i < 5; i++) { Console.WriteLine("i={0}", i); } } }}
很明顯它會輸出0到4五個數,
現在在迴圈體內增加類似先前討論中的if代碼,程式現在變為
namespace ConsoleApplication5{ class Program { static void Main(string[] args) { for (int i = 0; i < 5; i++) { Console.WriteLine("i={0}", i); if (++i==2) { Console.WriteLine( "at presenti={0}",i); } } } }}
這時輸出的是:
說明確實會影響迴圈次數。這是非常明顯的事情,但我還是實驗了一把。
所以,我只能說jQuery裡面for遍曆伺服器返回的結果時,它的迴圈變數沒有受到迴圈體內的改變的影響。但迴圈體內的i使用的值卻是從當前迴圈變數那裡複製過來的。
1天后。。。
在公司浩哥帶領下,第一個問題原因找出來了。
要從最外層的for迴圈說起。我們知道C#中用foreach對一個字典進行遍曆時,迴圈變數並不是單純的從0開始到所遍曆對象長度結束為止的連續數字,而是對應著當前所遍曆對象中的一條記錄。
我們再次用一個C#的ConsoleApplication來說明問題;
這裡定義一個Person類,再定義一個包含許多Person執行個體的列表,為了能夠用foreach遍曆,這裡的列表我們用System.Collections.Generic 命名空間下的泛型列表List<>,因為它實現了IEnumerable
介面,可以用foreach進行遍曆。
namespace ConsoleApplication6{ public class Person { public string Name { get; set; } } class Program { static void Main(string[] args) { var persons = new List<Person> { new Person(){Name = "Tom Cat"}, new Person(){Name = "Woody Bird"}, new Person(){Name = "Jemmy Duck"} }; foreach (var i in persons) { Console.WriteLine(i.Name); } } }}
無需多說,當我們在調用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>";
總結:在思索與尋求答案的過程中更正了自己不少錯誤的認識,也學到了很多東西。任何一個小問題都可以觸類旁通產生與之關聯的更多問題,需要我們去發現去思索。寫出來和大家分享,希望能協助到有類似情況的初學者。