如何在 Web API 调用中设置 Closure?

How Closure gets set in Web API call?

在发出像计时器 (serTimeout) 这样的 Web API 请求时,我不知道闭包变量是如何附加到函数 ([[Scope]]) 的。

function fun() {   
    function log() {console.log(callme) }; 
    console.dir(log); 
    setTimeout( log, 10000);
    const callme = "hi iam"
  }
fun()

当 const 没有被提升时,函数 log 可以访问变量 callme

我的想法是 fun() 被调用,它被压入调用栈,然后函数 log 被发送到 WebAPI 计时器函数,同时 fun 获取 运行 并在其执行上下文中设置 callme 变量。一旦 Timer 结束, fn log 被推送到回调队列,事件循环在检查调用堆栈是否为空后将 log 推送到调用堆栈执行。当 callme 附加到函数 log[=30 的闭包 [[Scope]] 时,我无法理解=]

恕我直言,hoisting 是一个非常糟糕的概念,但恰好有用。它会导致像你这样的误解。解释器的真实工作方式类似于我对这个相关问题的回答:JavaScript function declaration and evaluation order

对于你的具体例子,你提到:

when const do not get hoisted

这不是真的。当然,语言中并不真正存在提升,它只是程序员创建的一个概念,用于解释语言的行为方式。让我们看看当我们将解析分为编译和执行两个阶段时如何解释您的代码:

function fun() {   
    function log() {console.log(callme) }; 
    console.dir(log); 
    setTimeout( log, 10000);
    const callme = "hi iam"
  }
fun()

编译阶段

  1. 好的,我们有一个函数定义fun,让我们解析它的主体
  2. 好的,我们有一个函数定义log,让我们解析它的主体
    1. 好的,我们正在记录一个变量 callme。注意我们使用的变量
  3. 我们想在 10 秒后调用 log,注意函数 setTimeout 正在使用
  4. 我们定义一个常量callme

执行阶段

  1. 好的,我们正在调用 fun()
    1. 我们正在声明一个函数 log(),创建它的一个实例
    2. 我们输出log
    3. 的定义
    4. 我们正在调用 setTimeout(),找到一个名为 log 的 variable/function - 找到了,将 log 添加到事件队列
    5. callme的值设置为"hi iam"(注意不能在编译阶段设置值,因为它可能涉及执行代码,例如如果我们做const x = (1).toString(),const只是意味着我们不能在初始化之外设置值)
    6. 函数调用结束,我们检测到闭包中使用了callme,附上它
  2. 10 秒过去,OK 事件循环检测到超时,调用 log()
    1. 我们需要记录 callme - 在闭包中找到它,记录它。

这就是 callme 被拉入 foo 的方式。如果你坚持使用 hoisting 术语,你可以说如果有一个闭包然后 const 被提升。

但是,如果您将这种两阶段解释作为您的心智模型,那么您真的不需要知道什么时候发生提升和什么时候不发生提升的所有极端情况。我强烈建议阅读上面链接的答案,以更好地理解它是如何工作的。