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. Android中通过进程注入技术改动广播接收器的优先级

    前言 这个周末又没有吊事,在家研究了怎样通过进程的注入技术改动广播接收器的优先级.关于这个应用场景是非常多的.并且也非常重要.所以就非常急的去fixed了. Android中的四大组件中有一个广播:B ...

  2. redis研究记录

    1 redis安装 wget http://download.redis.io/redis-stable.tar.gz tar xvzf redis-stable.tar.gz cd redis-st ...

  3. Tomcat 内存设置

    大型项目或者使用了SSH 的话, Tomcat 可能会报 java.lang.OutOfmemoryError: PermGen Space Windows: 在 bin/catlina.bat 最上 ...

  4. 【转】CentOS 6 服务器安全配置指南

    原文连接: CentOS 6 服务器安全配置指南(通用) Linux 是一个开放式系统,可以在网络上找到许多现成的程序和工具,这既方便了用户,也方便了黑客,因为他们也能很容易地找到程序和工具来潜入 L ...

  5. A*算法的C语言实现

    #include ”stdio.h“ #include “conio.h” #include ”assert.h“ #include “stdlib.h” #define MAPMAXSIZE 100 ...

  6. 转:DNS拾遗

    最近帮朋友注册域名配置主机,碰到一些DNS上的一些概念,惭愧于有一些东西已经忘记是啥意思,于是决定重新学习一下DNS方面的基本概念. 常用概念: TTL: TTL为Time to live的缩写,网络 ...

  7. 用nohup执行python程序时,print无法输出

    python -u参数:关闭输出缓冲 nohup python -u test.py > nohup.out 2>&1 &

  8. JavaScript:获取系统当前时间,构造格式化的字符串

    var getNowFormatDate = function() {    var date = new Date();    var seperator1 = "-";    ...

  9. cocos2dx进阶学习之CCApplication

    继承关系 CCApplication-> CCApplicationProtocol 类主要成员变量 static CCApplication * sm_pSharedApplication; ...

  10. [置顶] jeecg-framework-3.3.2-RELEASE 最新版本发布

      平台介绍 JEECG(J2EE CodeGeneration)是一款基于代码生成器的智能开发平台,引领新开发模式(OnlineCoding模式->代码生成器模式->手工MERGE智能开 ...