在对一个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>";
总结:在思索与寻求答案的过程中更正了自己不少错误的认识,也学到了很多东西。任何一个小问题都可以触类旁通产生与之关联的更多问题,需要我们去发现去思索。写出来和大家分享,希望能帮助到有类似情况的初学者。