最近在邊學邊開發Nodejs,起初在代碼中大量使用for循環,review的時候感受很冗餘,便查了下nodejs中forEach方法,結果看到有網友提到nodejs關於循環的陷阱,回想本身在開發過程當中也曾遇到,只是當時沒有深究,如今再回顧思考下。node
http://cnodejs.org/topic/52e8e78a953654bb712654cb數組
循環陷阱閉包
for( var i = 0;i<files.length;i++){ fs.readFile(files[i],'utf-8',function (err,contents) { console.log(files[i] + ':' + contents); }) }
輸出結果爲異步
undefined:AAA
undefined:BBB
undefined:CCC
輸出 undefined 是由於 fs.readFile是一個異步方法,它的回調在for循環執行完畢後纔開始執行, 而此時變量 i=files.length, 超出了邊界。函數
看到網友專業的回答是「閉包會把當前的環境保存下來,原來的代碼裏面那個 for 建立了若干個閉包,可是每一個閉包共享上下文環境 i。由於 for (很大可能)會先跑完,因此運行回調函數的時候 i 已經變成了 files.length,這時候 files[i] 由於超過數組邊界,因此就 undefined 了。」性能
解決這個問題的方式收集了下,目前有3種:學習
1. forEach測試
files.forEach(function (filename) { fs.readFile(filename,'utf-8',function (err,contents) { console.log(filename+':'+contents); }) })
不存在上下文關係,不過經過測試,forEach的性能比for差, 另外 forEach也是同步執行的spa
2. funcation factorycode
function gencb(filename) { return function (err, contents) { if (err) //... console.log(filename + ':' + contents); }; }; for( var i = 0;i<files.length;i++){ fs.readFile(files[i],'utf-8', gencb(files[i])) }
每次閉包都不相同。
3. 修改原代碼
for( var i = 0;i<files.length;i++){ (function(i){ fs.readFile(files[i],‘utf-8’,function (err,contents) { console.log(files[i] + ‘:’ + contents); }); })(i); }
這種方式的名稱還不清楚,以後再學習學習。
關於 閉包
百度百科解釋:
閉包是指能夠包含自由(未綁定到特定對象)變量的代碼塊;這些變量不是在這個代碼塊內或者任何全局上下文中定義的,而是在定義代碼塊的環境中定義(局部變量)。「閉包」 一詞來源於如下二者的結合:要執行的代碼塊(因爲自由變量被包含在代碼塊中,這些自由變量以及它們引用的對象沒有被釋放)和爲自由變量提供綁定的計算環境(做用域)。
閉包的最簡單形式
function func(i) { return function(){ return i+1; } }
其中 i 是函數func的一個局部變量,又被返回的匿名函數所使用。
這裏也只是大概瞭解了下,深刻學習:
http://cnodejs.org/topic/567ed16eaacb6923221de48f
閉包的新理解:若是一個內部函數能夠訪問了它的外部變量,那麼它就是一個閉包。
閉包常常用於建立含有隱藏數據的函數(但並不老是這樣)。
var db = (function() { // 建立一個隱藏的object, 這個object持有一些數據 // 從外部是不能訪問這個object的 var data = {}; // 建立一個函數, 這個函數提供一些訪問data的數據的方法 return function(key, val) { if (val === undefined) { return data[key] } // get else { return data[key] = val } // set } // 咱們能夠調用這個匿名方法 // 返回這個內部函數,它是一個閉包 })(); db('x'); // 返回 undefined db('x', 1); // 設置data['x']爲1 db('x'); // 返回 1 // 咱們不可能訪問data這個object自己 // 可是咱們能夠設置它的成員