關於for循環中i=0與i=arr.length容易被忽視的bug


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早就已經遍歷完了。

 

希望這篇博文可以幫助需要的人,謝謝大家的閱讀,有不足之處請諒解,望大家指出來共同進步。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM