无法将函数内部的"this"与匿名函数一起使用



对node来说相当新.js并且在理解某些概念时遇到问题。

在下面的代码中,行console.log('Item ' + this.itemNo + ' is done.');显示未定义。

this.itemNo无法在setTimeout内访问的可能原因是什么?

async = require("async");
function doSomethingOnceAllAreDone(){
console.log("Everything is done.");
}
function Item(itemNo, delay){
this.delay = delay;
this.itemNo = itemNo;
console.log(`Created item ${itemNo} with ${delay} seconds`)
}
Item.prototype.someAsyncCall = function(callback){
setTimeout(function(){
console.log('Item ' + this.itemNo + ' is done.');
if(typeof callback === "function") callback();
}, this.delay);
};
// Create some items
let items = [];
for (let i = 0; i < 20; i++) {
items.push(new Item(i, Math.random() * 3000));
}
// Loop though items and create tasks
var asyncTasks = [];
items.forEach(function(item){
asyncTasks.push(function(callback){
item.someAsyncCall(function(){
callback();
});
});
});
// Add an extra task 
asyncTasks.push(function(callback){
setTimeout(function(){
console.log("Additional item is done.");
callback();
}, 3000);
});
// Execute the tasks in parallel and notify once done
async.parallel(asyncTasks, function(){
doSomethingOnceAllAreDone();
});

如果有嵌套函数,Javascript 中的范围非常重要。

(留意以下代码中的注释(

Item.prototype.someAsyncCall = function(callback) { // <-- Item object/class is accessible in lexical *this*
var itemNo = this.itemNo; // Accessible
setTimeout(function() { // <-- Here, we have a closure with its own scope and ITS OWN lexical *this*
var itemNoDuplicate = this.itemNo; // Not accessible as we are inside `setTimeout` scope
if(typeof callback === "function") callback();
}, this.delay);
};

有几种解决方案可以解决您的问题:

利用带有箭头功能的ES6图案:

Item.prototype.someAsyncCall = function(callback) {
// Arrow function in ES6 preserves lexical *this* of the closest parent
setTimeout(() => {
if(typeof callback === "function") callback();
}, this.delay);
};

利用bind(如果你想坚持使用ES5(

Item.prototype.someAsyncCall = function(callback) {
setTimeout(function() {
var itemNo = this.itemNo; // Yeaah! Its accessible    
if(typeof callback === "function") callback();
}.bind(this), this.delay);
// ^^^^^^^^^^^ -- We have bind the closest parents scope to this closure, // and now its accessible inside it 
};

请参考Atishay Jain在同一线程中的回答,以了解另一种方法。

代码已更新。使用这个

Item.prototype.someAsyncCall = function(callback){
var _this = this;
setTimeout(function(){
console.log('Item ' + _this.itemNo + ' is done.');
if(typeof callback === "function") callback();
}, this.delay);
};

或使用此

Item.prototype.someAsyncCall = function(callback){
setTimeout(() => {
console.log('Item ' + this.itemNo + ' is done.');
if(typeof callback === "function") callback();
}, this.delay);
};

最新更新