Javascript:如何在回调函数中访问局部变量

Javascript: How to access local variable in callback function?

本文关键字:函数 访问 局部变量 回调 Javascript      更新时间:2023-09-26

我有一个强大的形式来解析请求。然后,与此请求一起上传的文件。在强大的中,如果有文件,您可以收听事件。

var form = new formidable.IncomingForm({
    uploadDir: __dirname + '/temp',
    keepExtensions: true
});

这是我将聆听活动的地方

form.on('file', function(name, file){
   var file = file;
   fs.readFile(file.path, readFileCallback);
});
function readFileCallback(err, contents){
  console.log(file);
  if (err) throw err;
  ....
}

我的第一个代码是一系列回调函数,它有点难以阅读和维护,所以我切换了这种方法,我将声明函数然后将其调用为回调,而不是像这样:

form.on('file', function(name, file){
   fs.readFile(file.path, function(err, contents){
      console.log(file);
      if (err) throw err;
      .... 
  });
});

使用这种方法,我可以访问file的外部变量。我想知道两者在访问外部变量方面有什么区别。提前谢谢。

这是一个范围问题。代码可以访问函数中声明的变量、包含函数(如果有(、包含函数(如果有(等,然后访问全局变量。

在第一个示例中,readFileCallback是在form.on回调之外声明的,因此它无法访问form.on回调中的内容。

在第二个示例中,函数位于 form.on 回调,因此它确实可以访问其中的内容。

请注意,在第二个示例中,理论上每次调用回调时都会创建一个新函数。没关系,JavaScript 引擎在创建函数方面非常快(即使创建了单独的函数对象,好的引擎也会重用代码(。

通常,您希望在最外层的位置创建函数,在该位置可以访问所需的所有内容。所以在你的例子中,这将在form.on内部,但在readFile回调之外。这正是您的第二个示例所在。但是如果你愿意,你可以像你的第一个例子一样使用命名函数,只需把它放在form.on的回调

form.on('file', function(name, file){
   fs.readFile(file.path, readFileCallback);
   function readFileCallback(err, contents){
      console.log(file);
      if (err) throw err;
      .... 
   }
});

让我们举一个例子,其中所有内容都有一个简单的名称,并遵循两个调用:

function outer(outerArg) {
    function middle(middleArg) {
        function inner(innerArg) {
            console.log("innerArg = " + innerArg);
            console.log("middleArg = " + middleArg);
            console.log("outerArg = " + outerArg);
        }
        inner(middleArg.toLowerCase());
    }
    middle(outerArg.toUpperCase());
}

outer包含middle,其中包含innerouter调用middle(以及middle调用inner(。一个电话:

outer("Test1");
  1. outer得到参数"Test1"
  2. 它用"TEST1"调用middle
  3. 它用"test1"调用inner
  4. inner输出:

    innerArg = test1中间参数 = 测试1外部Arg = 测试1

到目前为止,很简单,但它比这更令人兴奋:如果middle返回一个调用inner的函数,而不是立即调用它,并且outer返回返回middle的返回值怎么办?

function outer(outerArg) {
    function middle(middleArg) {
        function inner(innerArg) {
            console.log("innerArg = " + innerArg);
            console.log("middleArg = " + middleArg);
            console.log("outerArg = " + outerArg);
        }
        function caller() {                         // ***
            inner(middleArg.toLowerCase());         // ***
        }                                           // ***
        return caller;                              // ***
    }
    return middle(outerArg.toUpperCase());          // ***
}

现在,调用outer根本没有任何输出:

var f = outer("Test2");

但是调用返回middle函数(caller(会:

f();

输出:

innerArg = test2中间参数 = 测试2外部Arg = 测试2

outer后争论仍然存在,middle回归!但更有趣的是:

var f1 = outer("Test3");
var f2 = outer("Test4");
f2();  // Note -- calling the second one first
f1();

输出:

innerArg = test4中间参数 = 测试4外部Arg = 测试4innerArg = test3中间参数 = 测试3外部Arg = 测试3

所以这意味着,在对outer的两次调用完成后,仍然存在两个outerArg,以及两个middleArgs。如何?

它们存在于附加到函数的对象上:

  • 调用outer会创建一个执行上下文(一个对象(,除其他外(并省略了很多细节(保存了该调用outer的参数和局部变量。我们称之为"外部语境"。它还具有对包含它的执行上下文的引用(在我们的代码中为全局上下文(。通常,当函子返回时,该对象会被清理...
  • 。但是outer创建了一个函数,middle.创建函数时,当前执行上下文将附加到该函数。这就是它如何在外部上下文中访问变量等。
  • outer调用middle,创建一个内部执行上下文,middle创建另外两个函数(innercaller(,每个函数都附加了内部上下文。 然后middle返回 caller ,因此caller在对middle的调用完成后存在。由于caller具有对内部执行上下文的引用,因此该上下文将继续存在(就像任何其他对象一样(,即使middle已返回。由于该上下文有对inner的引用,inner继续存在。
  • outer返回 middle 的返回值(即 caller (,因此这意味着当outer返回时caller仍然存在,这意味着它引用的内部上下文仍然存在,这意味着inner仍然存在,外部上下文仍然存在,因为内部上下文具有对它的引用。

。这就是f1f2outer返回后访问这些参数的方式:当您运行它们时,它们会在附加到它们的上下文中查找值。