【问题标题】:How Closure gets set in Web API call?如何在 Web API 调用中设置闭包?
【发布时间】:2020-05-20 06:24:48
【问题描述】:

在发出类似计时器 (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

的闭包 [[Scope]] 时,我无法理解

【问题讨论】:

  • [[Scope]] 只是对父函数执行上下文的引用。正如你所说,callme 被添加到它(它也被提升,只是不同)

标签: javascript closures


【解决方案1】:

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

对于您的具体示例,您提到:

当 const 不被提升时

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

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

编译阶段

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

执行阶段

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

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

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

【讨论】:

  • 这是很好的解释,非常感谢它现在清楚地理解了。只有一个查询,因为在编译阶段和执行阶段 step3 未设置 callme 变量,使用 setTimeout fn 强制日志函数从 JS 环境移动到存在计时器的 webAPI 世界,然后将 callme 设置为“hi i am”和 fun()从调用堆栈中删除。那么日志如何在执行阶段第 2 步访问 callme
  • 只要有闭包,就不会从堆栈中删除函数或变量。相反,它被引用计数并最终被垃圾收集器删除。有关闭包的内存模型的更详细解释,请参阅我对这个问题的回答:stackoverflow.com/questions/26061856/…(很遗憾,我的一些最详细的答案被写成与该主题无关的问题的切线,这使得它们很难找到即使对我来说)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
  • 2017-06-28
  • 2017-08-07
  • 2017-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多