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

时间:2016-09-24 06:14:32

标签: javascript node.js

我有一个解析请求的formidable表单。然后这个请求就是正在上传的文件。在formidable中,如果有文件,你可以收听一个事件。

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。我想知道两者在访问外部变量时有什么区别。提前谢谢。

1 个答案:

答案 0 :(得分:1)

这是范围的问题。代码可以访问函数内声明的变量,它包含的函数(如果有的话),它包含的函数(如果有的话),等等,然后是全局变量。

在您的第一个示例中,readFileCallbackform.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获取了arg "Test1"
  2. 使用middle
  3. 调用"TEST1"
  4. 使用inner
  5. 调用"test1"
  6. inner输出:

    innerArg = test1
    middleArg = TEST1
    outerArg = Test1
    
  7. 到目前为止,这么简单,但它比这更令人兴奋:如果middle返回一个调用inner的函数,而不是立即调用它,outer该怎么办?返回返回middle&#39}的返回值?

    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
    middleArg = TEST2
    outerArg = Test2
    

    outermiddle返回后,参数仍然存在!但它更有趣:

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

    输出:

    innerArg = test4
    middleArg = TEST4
    outerArg = Test4
    innerArg = test3
    middleArg = TEST3
    outerArg = Test3
    

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

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

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

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