无法在带有匿名函数的函数内使用“ this”

时间:2018-10-11 11:02:17

标签: javascript node.js asynchronous

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();
});

2 个答案:

答案 0 :(得分:2)

如果有嵌套函数,则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的答案,以了解另一种方法。

答案 1 :(得分:0)

代码已更新。使用这个

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);
};