繁体   English   中英

JavaScript:了解闭包和提升

[英]JavaScript: Understanding closures and hoisting

我知道函数声明被提升到其范围的顶部。 这允许我们在JavaScript中实际声明之前使用这些函数:

sayHello(); // It works!

function sayHello() {
  console.log('Hello');
}

我也理解闭包使函数能够保留对在同一范围内声明的变量的引用:

function outerFxn() {
  let num = 0;

  function innerFxn() {
    console.log(num);
    num++;
  }

  return innerFxn;
}

const logNum = outerFxn();
logNum(); // 0
logNum(); // 1
logNum(); // 2

到现在为止还挺好。 但是这里有些奇怪,我希望有人可以准确地解释发生了什么......

场景1:可理解的闭包

function zero(cb) {
  return setTimeout(cb, 0);
}

function test1() {
  let txt = 'this is a test message';

  function log() {
    console.log(txt);
  }

  zero(log);
}

在上面的示例中, log函数保留对其创建范围的引用,并保留txt变量。 然后,当稍后在setTimeout执行时,它会成功记录txt变量的值。 大。 那就是这个......

情景2:发生了什么?

function zero(cb) {
  return setTimeout(cb, 0);
}

function test1() {
  function log() {
    console.log(txt);
  }

  let txt = 'this is a test message';

  zero(log);
}

我已经将log函数声明移到了作用域的顶部(无论如何它都会被提升,对吧?), 然后我在它下面声明了txt变量。 这一切仍然有效,我不知道为什么。 如何log保持到基准txt时可变let的和const的不吊上来? 闭合范围是作为一个整体进行分析的吗? 我可以在这里逐步清楚地了解JavaScript引擎的工作原理。 谢谢SO土地!

离开test1函数后,它是范围的一部分。 如果它在那一点上与varletconst一起使用并不重要。 由于已对整个身体进行了评估,因此该变量存在于范围内。

如果在评估let声明之前尝试使用log ,则会出现错误。

编辑 :从技术上讲,使用letconst声明的变量在范围内,但它们是单元化的,如果您尝试访问它们会导致错误。 只有在你得到声明它们被初始化并且你可以访问它们之前。 所以它们总是在范围内,只有在评估声明之前才可用。

“封闭范围是否整体分析?” - 是的 闭包保留了您(词汇上)离开时的范围。 在您的示例中,当您在test1到达closing }时, txt确实存在,因此它在范围内,并且log访问它时没有问题。

注意上面的“词法”:绑定是在运行时之前完成的,只有重要的是你的块结构。 所以即使这样也行不通,但它不应该从“动态”的角度出发:

function test1() {
    function log() {
        console.log(txt);
    }

    zero(log);
    let txt = 'this is a test message';
}

场景2中,您正在做:

  1. let txt = 'this is a test message' ,这意味着txt将成为test1()范围的一部分。
  2. 同时,您声明了log() ,它可以访问其父test1()的范围。

那么在运行时会发生什么? 将评估test1() ,因此log()将可以访问test1()的范围。 这意味着txt可用于log()立即使用。

提示:调试它,放置一些断点,看看会发生什么。

编辑:你也可以考虑在log() ,没有定义txt ,因此它的值应该是未定义的......对吗? console.log(txt)工作输出的this is a test message是由于上面对范围的解释。 将变量声明在函数作用域的顶部,并将函数声明在作用域的底部,因为它们将首先进行评估,这总是很好的做法。 在这种情况下考虑人为因素,最佳实践也可以意味着:让您/任何人通过阅读它来了解代码的作用。

这是一个时间/执行顺序的事情。 想想就好

function test1(){
    var context = { }; 

    function log(){
        if(context.hasOwnProperty("txt")){
            console.log(context.txt); 
        }else{
            throw new Error("there is no value 'txt' declared in this context");
        }
    }

    context.txt = 'this is a test message';
    log();
}

代码与未提升的变量txt 那时,执行loglet txt在适当的函数上下文中声明。 即使没有悬挂也可以使用。 函数log不存储对变量本身的引用,而是存储整个周围的执行上下文,并且此上下文存储变量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM