【问题标题】:Closure of function defined as an argument定义为参数的函数的闭包
【发布时间】:2018-08-12 07:33:09
【问题描述】:

免责声明:这个问题纯粹是出于好奇,并且与 javascript 的工作原理有很大关系。

我了解以下代码为何有效。由于闭包, foo 可以访问 a 所在的范围。这是有道理的。

var a = 10

var foo = function(){
console.log(a);
}

setTimeout(foo,1000)

但是,我想知道为什么以下内容也有效(稍后解释)。

var a = 10
setTimeout(function(){
console.log(a);
},1000)

函数在接收它的函数的参数中定义,本质上从来不是包含a 的范围的闭包。我们知道,当一个函数接收一个参数时,它会为该参数创建一个局部变量,例如

var outerVar="5"

var bar = function(a){
//implicitly, var a = outerVar happens here
console.log(a)
}

bar(something);

所以按照这个逻辑,传递给setTimeout 的函数无法访问a,但它确实可以。

我怀疑当一个函数在参数空间中定义时会发生什么,它实际上是在被分配为参数之前定义的,但没有证据证明这一点。任何指针都非常感谢。

非常感谢。

【问题讨论】:

  • 您的问题在于“函数是在接收它的函数的参数中定义的,本质上从来不是包含 a 的范围的闭包。”这不是真的。函数的定义是在“a”上发生闭包的地方。这与“a”在同一范围内。
  • @bhspencer 是的,但是定义到底发生在哪里?我改写了这个问题以显示我的意思。看看这个:stackoverflow.com/questions/49106298/…

标签: javascript closures


【解决方案1】:

这不完全是关闭,但它很接近。

严格来说,闭包是指变量的作用域结束,但仍被封闭在仍然存在的内部函数中:

function createTimer() {
  let counter = 0;
  return function() {
    return counter++;
  }
}

const timer = createTimer(); // function() { ... }
console.log(timer(), timer(), timer()); // 0, 1, 2

定义counter的函数已经返回,作用域结束,正常情况下counter应该已经死掉,垃圾回收了。但是从createTimer() 返回的内部函数仍然有一个对它的引用,来自封闭范围,这是一个闭包。


在 JavaScript 中,每个函数都可以访问其所有祖先的所有作用域,这就是您在此处看到的。

传递给setTimeout() 的函数可以访问a,因为a 是在它周围的范围内定义的。

【讨论】:

  • 这是对闭包的正确解释,是的。然而,我面临的挑战如下。想象一下 setTimeout 有自己的局部变量 'a' 等于 50 。在实践中,我的场景 A 和 B 都将打印出 10。但我希望场景 B 打印出 50,因为您提到的它的封闭范围是 setTimeout 而不是全局范围。在我看来,我似乎明白了闭包背后的一般概念,但是当在参数空间中定义一个函数时,在我看来,这个函数实际上是在其传递的函数中定义的,而不是在外部。
  • 但是您的setTimeout 回调不接受任何参数,在函数体内也没有定义名为a 的变量(如果有,它们会隐藏 来自封闭范围的a)。
【解决方案2】:

当您查看 javascript 代码并了解其工作原理时,我认为最好的方法是首先了解 javascript 引擎的工作原理。

首先它遍历代码并将所有变量分配给范围,然后在第二个跟踪中它根据范围分配值。

所以在你的第一个代码中, 引擎首先遍历并赋值 var a 到全局范围, var foo 作为全局范围, 然后当 setTimeout 运行时,它调用 foo 函数并将 a 的值记录为全局 a 的值,因为它没有任何本地“a”,因此检查词法范围。

在您的第二个代码中,

Var a 再次是全局范围的 这次没有其他声明。 在第二次遍历中,它将值 10 分配给 a 并解释 settimeout 并打印值

在您的第三个代码中, 与第二个相同,除了“foo”给 settimeout 函数,你写了你的回调函数然后 n 本身。

当它执行 setTimeout 时, 您的每个代码在它们正在访问的全局范围内都有“a”的值。

【讨论】:

  • 当然可以,但问题是,在我的第二种情况下,该函数是否在 setTimeout 的上下文中查找第一个,然后在全局范围内查找?我希望这是真的,但事实并非如此。
  • 为什么不呢?只需在 setTimeout 中添加另一个变量 var a = 50;并检查答案。它将打印 50,因为它现在有一个局部变量 a
  • 我也是这么想的。你会感到惊讶。看这里stackoverflow.com/questions/49106298/…。我不得不改写这个问题
  • 如果你在回调之前放置一个调试器,你会看到没有形成闭包。您作为参数提供的函数是函数的本地函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
  • 2019-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
相关资源
最近更新 更多