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. Linux基本配置和管理 1---- Linux网络基本配置

    1 IP编址 1 IP编址是一个双层的编址方案,一个IP编址标识一个主机(或一个网卡接口) 2 现在应用最为广泛的是ipv4,已经开始逐步香ipv6切换 3 ipv4地址为32位,ipv6为128位 ...

  2. Android apk逆向实战

    简介 逆向Android apk其实是一个分析Android apk的一个过程,必须了解Android程序开发的流程.结构.语句分支.解密原理等等. 功能 破解一个注册验证程序(自写一个简单的注册验证 ...

  3. POJ 3667 splay区间盘整运动

    Hotel Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12446   Accepted: 5363 Descriptio ...

  4. git 使用过程(三、文件的添加 修改)

    1.库中添加文件 在目录下新建一个文件 如 testfile.txt .输入命令:① git add testfile.txt  ②git commit -m "这里是你提交的说明" ...

  5. 提高你开发效率的十五个Visual Studio 2010使用技巧

    提高你开发效率的十五个Visual Studio 2010使用技巧 相信做开发的没有不重视效率的.开发C#,VB的都知道,我们很依赖VS,或者说,我们很感谢VS.能够对一个IDE产生依赖,说明这个ID ...

  6. (Problem 1)Multiples of 3 and 5

    If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The ...

  7. [置顶] Android开发之XML文件的解析

    Android系统开发之XML文件的解析 我们知道Http在网络传输中的数据组织方式有三种分别为:XML方式.HTML方式.JSON方式.其中XML为可扩展标记语言,如下: <?xml vers ...

  8. 幻世(OurDream)2D图形引擎使用教程11——播放媒体文件(1)

    声明:本教程版权归Lizcst Software Lab所有,欢迎转载,但是转载必须保留本段声明文字,并注明文章来源:http://blog.csdn.net/kflizcst 谢谢合作! 播放媒体是 ...

  9. getElementById的缩略

    谁都知道document.getElementById这个方法写起来比较麻烦,所以有时候我们会使用jq或者自己写一个小函数去封装,但是我这次说的缩略不同于往常的函数封装 在以前,我做过很多这样的尝试, ...

  10. MUI 列表页面绑定接口数据

    1.我们先看效果 2.功能分析 主要分为3步 数据初始化 下拉刷新 上拉加载 接口数据示例: { "List": [ { "Id": 9, "Orde ...