var fs=require('fs');
var path=['c:/', 'd:/', ''];
for (var i = 0; i < path.length; i++) {
if (path[i].length > 0) {
// 可以得到值
console.info(path[i]);
fs.exists(path[i], function(exists) {
if (exists) {
// 为何得到的是undefined?如何将下面的变量正确输出呢?
console.info('get path inside', path[i]);
} else {
console.error('path not exists');
}
})
}
}
输出怎么是undefined?
14 回复
bind 进去就好了
fs.exists(path[i], function(exists) {
if (exists) {
console.info('get path inside’, this.path);
} else {
console.error(‘path not exists’);
}
}.bind({ path:path[i] }) );
JavaSript可以使用调用这个闭包函数时的Context中的变量。否则,闭包中引用了path[i]
,如果path === undefined
,会报错的。
你的示例代码中出现的这种现象,归根结底是因为把循环和异步调用函数结合在一起使用。由于fs.exists()
是一个异步函数,所以它只会在真正被调用时再去查看Context中的变量取值。此时,循环已经执行完毕,变量的取值分别是i = 3; path[3] = undefined;
。因此,返回一个path not existed
就是理所当然。
- 验证
i = 3
// 验证循环与异步函数一起使用时的context
var fs = require('fs');
var paths=['c:/', 'd:/', ''];
for (var i = 0; i < paths.length; i++) {
if (paths[i].length > 0) {
fs.exists(paths[i], function(exists) {
if (exists) {
console.info('get path inside', paths[i], i);
} else {
console.error('path not exists', i);
}
});
}
}
- 解决方法:使用
Array.map()
取代for
循环
var fs = require('fs');
var paths=['c:/', 'd:/', ''];
paths.map(function(dest) {
fs.exists(dest, function(exists) {
console.log(dest, exists);
});
});
- 如果目标对象不是一个数组,没法使用
Array.map()
,可以用Async等模块控制调用异步函数的流程。
很明显这里形成了一个闭包,由于回调函数是异步的,在执行回调函数时其所在的上层作用域已经执行完退出了,但是由于闭包的原因,回调函数仍然引用了上层作用域 的变量对象,但此时变量的值已经改变,在这里就是i=3,所以输出undefined,此时我们可以在循环内部开辟一个执行作用域,把包含回调函数的方法放入这个作用域中,,这样就进行了作用域隔离,可以避免这个问题,因此可以这样修改:
var fs=require('fs');
var path=['c:/', 'd:/', ''];
for (var i = 0; i < path.length; i++) {
if (path[i].length > 0) {
// 可以得到值
console.info(path[i]);
(function(i){
fs.exists(path[i], function(exists) {
if (exists) {
console.info('get path inside', path[i]);
} else {
console.error('path not exists');
}
});
})(i);
}
}