无法在具有匿名功能的函数内使用“this”

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了无法在具有匿名功能的函数内使用“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);
};

以上是关于无法在具有匿名功能的函数内使用“this”的主要内容,如果未能解决你的问题,请参考以下文章

[沫沫金]JS匿名函数this调用变量报错

如何在匿名函数中访问“this”? [复制]

闭包的使用

箭头函数、普通函数和this的区别

闭包和匿名函数

匿名函数中的“this”?