【问题标题】:understanding Closure with call stack用调用栈理解闭包
【发布时间】:2020-07-24 04:34:21
【问题描述】:

我正在尝试了解与 javascript 中的调用堆栈相关的 javascript 闭包。

我遇到了一个闭包的例子

function a() {
  let grandpa = 'grandpa'
  return function b() {
    let father = 'father'
    return function c() {
      let son = 'son'
      return `${grandpa} > ${father} > ${son}`
    }
  }
}

let x = a();
let y = x();
y();

现在根据我对调用堆栈的理解,当我们调用函数 a() 时,它会被压入堆栈,然后当我们运行函数 'b' 时,它会被压入 'a' 然后函数 'c' 上'b'。所以在我看来它应该是这样的

但是,我遇到的这个例子的解释显示了闭包的含义:-

“当我们调用函数'a'时,我们得到的返回是函数'b',在返回'b'之后,函数'a'从堆栈中弹出,因此它的变量环境被删除(垃圾收集的概念 -标记和扫描算法)。 但是,函数 'c' 仍然可以访问变量 'grandpa' 和 'father' "

根据我的理解,JS调用栈遵循先进后出(或者说后进先出)的概念。 因此,如果调用堆栈遵循 LIFO,那么为什么 a() 在 c() 之前从堆栈中弹出,不应该先弹出 c() 然后 b() 然后 a() 吗??

感觉这里的闭包解释和我对调用栈的理解是相反的。

如果有人能解释这个闭包示例以及调用堆栈,我将不胜感激?

【问题讨论】:

  • 因为a() 执行完毕。一旦完成,它就会从调用堆栈中删除 - 完成。当您调用b()(通过x()()时,您正在执行一个new函数调用,该函数调用完成并返回c(将其分配给y)。
  • @VLAZ 所以如果函数已经完成执行,即使它首先进入堆栈也会被删除??您能否确认我对堆栈的理解是否正确??
  • 堆栈是 LIFO,是的 I've wrote about it before。如果你有类似a = () => b() 的东西,那么调用a() 会将a 推入堆栈,然后b 一旦b 得到解决,你就会回到a 的堆栈框架,一旦完成,你回到了前一个堆栈帧(如果有的话)。但是,您实际上有 a =() ={}b = () =>{} 然后依次调用它们,这样您就不会为它们获得嵌套的堆栈帧。
  • 闭包是关于作用域链的。它们与调用堆栈无关。

标签: javascript closures callstack


【解决方案1】:

闭包是关于从作用域链或词法环境访问变量。在上面给出的例子中

let x = a() => x 的输出是function b(){given code}; let y = x() => y 的输出是function c(){given code};

a() 执行完毕后,我们的调用栈将为空。所以一切都会从堆栈中删除。现在说在千行代码之后我们突然想运行我们的函数y()

我们有我们的 y

function c() {
 let son = 'son'
      return `${grandpa} > ${father} > ${son}`
}

此时,如果我们调用y(),将创建一个c 的执行上下文,但调用堆栈中将没有任何内容可供$grandpa$father 引用。但是它仍然会打印正确的输出,因为即使返回函数,内部函数引用的变量也不会被垃圾收集,它们将存储在某个地方以便以后使用。 这是关闭。

【讨论】:

    猜你喜欢
    • 2018-06-30
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-16
    • 2022-11-03
    • 2018-09-05
    相关资源
    最近更新 更多