本來以為for循環可以很好的解決一切問題,直到今天遇到了這段代碼,刷新了我對for循環的認識,話不多說,直接上代碼
var arr = [];
for(var i = 0;i<10;i++) {
arr[i] = function()
{
console.log(i)
}
}
arr[3]();
大家看上面這段代碼,我先聲明了一個空數組,然后把它放在循環里面,循環添加函數作為arr數組的數據,第一印象看到的時候,肯定不少人會毫不猶豫的說出3這個答案,
因為索引為3的時候console.log剛好是3嗎,想想差點自己都信了,但是,結果呢?
結果明顯是10,那么原因是什么呢?由於for循環中的i變量是用var聲明的,此時的 i 在全部范圍內都有效,所以每一次循環,新的i值會覆蓋舊的i值,導致最后輸出是最后一輪的i的值,當最后輸出的時候i 已經變為10了
那么,有沒有解決的辦法呢,辦法是有的,且聽我慢慢道來:
方法一:閉包
再用閉包的時候,你首先要了解什么是閉包,所謂“閉包”,指得是擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),它可以訪問局部變量,並且在訪問的同時使局部變量的內存不被釋放。那么,怎么用閉包呢,簡單點來講,就是嵌套函數。代碼如下:
var arr = []; for(var i = 0;i<10;i++){ (function(val) { arr[i] = function() { console.log(val) } })(i) } arr[3]();
將for的循環內容方法放在一個自調用的匿名函數里面,這個時候的val是由 i 來傳遞的,此時的變量val可以被訪問,而且內存不被釋放,也就是說,val沒有覆蓋一說,所以自然而然的輸出結果是3
方法二:ES6當中的let
ES6現在的推行范圍不是很廣,而且許多低版本的瀏覽器無法識別ES6,所以此方法用的時候是有局限性的,建議你把瀏覽器的版本升為高版本;
好嘞,言歸正真,我們先來了解一下什么是ES6當中的let:
let全稱為代碼塊作用域,顧名思義他是作用域代碼塊的,它和var的用法相似,但是在同一個代碼快中不能出現重名的let變量;代碼如下:
var arr = []; for(let i = 0;i<10;i++) { arr2[i] = function() { console.log(i) } } arr[3]();
變量i是let聲明的,當前i只在本輪循環中有效所以每次循環的i其實是一個新的變量,所以最后輸出的是3
才疏學淺,目前只發現了這兩種方法,如果有新方法或者不對的地方,請指教。