for循环中的这两种写法

for(var i=0,len=arr.length;i<len;i++){

}

上面这种是最为常见也是初学者经常写的

而下面这种写法,在性能上则是比上面的更好,然而我们今天要讨论的并不是这两者性能上的区别,而是第一种写法有时候会带来一些让人毫无头绪的bug。

for(var i=arr.length;i--;){

}

今天写一个h5的视频弹幕的时候就遇到了这个bug,先贴代码

    function drawScreen() {
setTimeout(function () {
//绘制视频
context.drawImage(videoEle, 0, 0, 960, 720)
//绘制弹幕 for (var i = 0, len = messages.lengthg; i < len; i++) {
// for (var i = messages.length; i--;) {
var message = messages[i]
context.fillText(message.value, message.x, message.y)
messages[i].x -= 10
if (messages[i].x <= 0) {
messages.splice(i, 1)
}
} setTimeout(arguments.callee, 30)
}, 30)
} drawScreen()

简要解释一下这段代码的意思,我主要想写一个视频弹幕,所以利用canvas替代了原有的视频video标签。

context.drawImage(videoEle, 0, 0, 960, 720)

这里主要是用drawImage把原有视频的画面一张一张的拿出来之后重新绘制在画布上,相当于取代了原有的video标签。

for (var i = 0, len = messages.length; i < len; i++) {
var message = messages[i]
context.fillText(message.value, message.x, message.y)
messages[i].x -= 10
if (messages[i].x <= 0) {
messages.splice(i, 1)
}
}

这段则是我们将要讨论的代码。messages是一个数组,保存着弹幕的信息,包括弹幕的位置,弹幕的内容都在这里保存着。

我们先用一个for循环把messages的东西都取出来,之后就开始对数组里面保存的每一个弹幕进行绘制在canvas上。

在此处,当某条弹幕的内容超出了屏幕的宽度的时候,我们需要把它从数组中删除掉从而节约资源,这时候问题就出现了。

假设现在我们有三条弹幕的信息,分别简单的设为1,2,3

var messages=[1,2,3]

当第一条弹幕消息超出了屏幕的宽度,这时的for循环中调用这行代码把第一条弹幕消息从数组中删除了

messages[i,1]

所以,此时弹幕数组变成了,注意这里,很重要!!

messages    //    [2,3]

虽然删除了第一个弹幕消息,但是原来的for还在继续执行,这时的for循环执行到了i=1的情况。

由于原数组变成了

messages    //    [2,3]

所以messages[1]读取到的就是原来的弹幕3。

messages[1]  //3

到了这一步,获取你已经发现问题了。嗯,我们的for循环还在继续,所以此时的i=2,那么问题就来了messages[2]到底是谁呢?

messages    //    [2,3]

很明显,messages只有messages[0]和messages[1],所以很抱歉,此时的messages[3]获取到的是undefined,好了,接着看我们原来的代码

for (var i = 0, len = messages.length; i < len; i++) {
var message = messages[i]
context.fillText(message.value, message.x, message.y)
messages[i].x -= 10
if (messages[i].x <= 0) {
messages.splice(i, 1)
}
}

messages[3]是undefined,所以很明显此处的

context.fillText(message.value, message.x, message.y)

肯定会出错。undefined怎么可能会拿得到value,x,y等值呢?所以浏览器就给我报了个错

Uncaught TypeError: Cannot read property 'value' of undefined

嗯,很好,我们bug的根源已经找到了,那么该怎么修改呢?这就扯到开篇说的var i=len了。所以此处我们bug的改法是这样的

for (var i = messages.length; i--;) {
var message = messages[i]
context.fillText(message.value, message.x, message.y)
messages[i].x -= 10
if (messages[i].x <= 0) {
messages.splice(i, 1)
}
}

请注意,for循环的地方改变了,这样的写法bug就没了,因为我们的i值是从最后一个开始遍历,所以此处当第一条弹幕被删除的时候,弹幕2与弹幕3早就已经遍历完了。

希望这篇博文可以帮助需要的人,谢谢大家的阅读,有不足之处请谅解,望大家指出来共同进步。

关于for循环中i=0与i=arr.length容易被忽视的bug的更多相关文章

  1. for循环中i--的妙用 及 两变量互换数值的问题

    int[] array = new int[4]; for(int i = 0; i < array.length; i++){ array[i] = (int)(Math.random() * ...

  2. Python 之 for循环中的lambda

    第一种 f = [lambda x: x*i for i in range(4)]  (如果将x换成i,调用时候就不用传参数,结果都为3) 对于上面的表达式,调用结果: >>> f ...

  3. JavaScript形而上的For循环中的Break

    break相当于循环中的GOTO,需避免使用. 下面是一个break使用例子. 找出第一个months小于7的项目. const cats = [ { name: 'Mojo', months: 84 ...

  4. vue 如何在循环中 "监听" 的绑定v-model数据

    vue 如何在循环中 "监听" 的绑定v-model数据 阅读目录 vue 如何在循环中 "监听" 的绑定v-model数据 1. 普通属性的值进行监听 2. ...

  5. Java循环中try...finally...遇到continue

    一段很简单的代码,先自己在大脑中给出结果: for (int i = 0; i < 5; i++) { System.out.println("enter: i=" + i) ...

  6. 浅谈循环中setTimeout执行顺序问题

    浅谈循环中setTimeout执行顺序问题 (下面有见解一二) 期望:开始输出一个0,然后每隔一秒依次输出1,2,3,4. for (var i = 0; i < 5; i++) { setTi ...

  7. JS如何获取PHP循环中的ID

    JS如何获取PHP循环中的ID  kaalrz 二路公交车    结帖率:83.33%   首先抱歉,因为昨天那帖图片几次都不能用,修改到不能再次修改,今天早上回帖又提示没有这个帖,只好重发一次. 如 ...

  8. for循环中 i++和++i 是否有区别?

    正常情况下  i++和++i是有区别的: 前者是:先引用,后增加, 后者是:先增加,后引用, 但是在for循环中: for(var i=0;i<10;i++){ System.out.print ...

  9. 循环中的let和const声明

    一.循环中的let声明 每次循环的时候let声明都会创建一个新变量i,并将其初始化为i的当前值,所以循环内部创建的每个函数都能得到属于他们的i的副本. 最初的: for (var i = 0 ; i ...

随机推荐

  1. java学习之动态代理模式

    package com.gh.dynaproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Metho ...

  2. 简单浮点数除法模拟-hdu-4493-Tutor

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4493 题目意思: 给小数点后两位的12个月的工资,求出平均工资,输出离小数点后第二位最近的两位小数, ...

  3. MFC下的日历表

    // CalenderDlg.h : header file // #if !defined(AFX_CALENDERDLG_H__8DC8F113_2A47_45B8_8266_75CB406D68 ...

  4. BZOJ 3403: [Usaco2009 Open]Cow Line 直线上的牛( deque )

    直接用STL的的deque就好了... ---------------------------------------------------------------------- #include& ...

  5. 使用ARC必须遵守的规则

    l        不可以再显示调用dealloc.或实现调用retain.release.retainCount.autorelease这些方法.也不能使用@selector(retain), @se ...

  6. ios 判断,qq,银行卡,手机号,邮编,生日,数字,字符串,护照, email

    http://blog.csdn.net/dyllove98/article/details/8635079 IdentifierValidator.h // //  IdentifierValida ...

  7. Chapter 11 迪米特法则

    迪米特法则也叫最少知识原则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用.如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用. 迪米特法则首先强调的前提是 ...

  8. Linux内核源代码解析之——我与神童聊Linux内核

    本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11619609 我的朋友里,至少有2.5个神童. 有的 ...

  9. C++成员变量与函数内存分配

    关于结构体和C++类的内存地址问题 C++类是由结构体发展得来的,所以他们的成员变量(C语言的结构体只有成员变量)的内存分配机制是一样的.下面我们以类来说明问题,如果类的问题通了,结构体也也就没问题啦 ...

  10. [置顶] CSS+DIV总结

         HTML在Web飞速发展的过程中起着重要作用,有着重要地位.HTML初衷是为了表达标签(<p>.<table>)的内容信息.同时文档布局由浏览器来完成,不使用任何格式 ...