javascript - 无法在具有匿名函数的函数内使用 "this"

标签 javascript node.js asynchronous

刚接触 node.js 并且在理解某些概念时遇到问题。

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

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

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

您的问题有几个解决方案:

Make use of ES6 pattern with arrow function:

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

Make use of bind (if you want to stick with 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 
};

Please refer to Atishay Jain's answer in this same thread to know another way to do it.

关于javascript - 无法在具有匿名函数的函数内使用 "this",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52758445/

相关文章:

javascript - 检查主数组中的所有元素在由标识符 javascript 标识的子数组中是否至少出现一次

node.js - 为什么有些 Nodejs 文件的底部有这个?

node.js - 测试使用 Kue 的 Node.js 应用程序

javascript - 使用 ajax 在服务器端接收 json 时出错

javascript - 使用 Array.isArray 和 instanceof Array 的区别

javascript - DataTables JS 在 PHP 应用程序中无法正确排序日期

javascript - Express/EJS 数组和列表单击追加

javascript - 使用 fetch 获取页面设置

javascript - 如何从异步调用返回响应?

asynchronous - 同步读/写端口时避免递归?