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学习之IO之File类一

    File的操作 package com.gh.file; import java.io.File; import java.io.IOException; /** * File操作 * @author ...

  2. sqlite3 脚本的使用

    本来想找个专门查看数据库的工具的,后来发现脚本也非常easy,就直接用脚本了. sqlite3的脚本能够方便的查看当前数据库里的内容,使用起来事实上非常easy,这一列举一些最简单的使用方法,以后假设 ...

  3. JavaScript语言基础知识6

    在前面的章节中,我们知道JavaScript代码,字符和数字值当添加,将计值转换成字符,即用户输入的数目值它们被转换为字符. 如今我们要做这种样例,我想将1和2相加: <HTML> < ...

  4. JS+CSS打造三级折叠菜单,自动收缩其它级 js

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...

  5. Android访问网络

    Android中访问网络用的是HttpClient的方式,即Apache提供的一个jar包.安卓中继承了改jar包,所以安卓adt中不需要专门import该jar,直接就可以使用. 以下是MainAc ...

  6. Chapter 10 模版方法模式

    我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑用模版模式来处理. 模版方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模 ...

  7. QT全平台设置图标,全平台静态编译 good

    1.  概述 当我们用QT写好了一个软件,要把你的程序分享出去的时候,不可能把编译的目录拷贝给别人去运行.编译好的程序应该是一个主程序,加一些资源文件,再加一些动态链接库,高大上一些的还可以做一个安装 ...

  8. 转:python中函数curry化

    1 柯里化(Currying) 一个函数有多个参数,我们希望能固定其中几个参数的值. from functools import partial def foo(a,b,c): return a+b+ ...

  9. Pathchirp—有效的带宽估计方法(二)

    上一个blog介绍了有效带宽估计方法:pathload.http://blog.csdn.net/ice110956/article/details/11126491. 做一个小小的总结:pathlo ...

  10. 详解iOS7升级细节:引领视觉革命

    下星期我们将看到的正式版将和WWDC上看到的大不相同.苹果六月份发布了全新版本的iOS操作系统——这是从2007年首次发布以来的最大的一次调整和改进.这次的改变招致许多批评.许多设计师在网站上晒出了他 ...