Skip to content

闭包、作用域、上下文

作者:江月迟迟
发表于:2024-12-10
字数统计:961 字
预计阅读4分钟

闭包

学习Javascript闭包(Closure) - 阮一峰的网络日志 (ruanyifeng.com)

闭包 - JavaScript | MDN (mozilla.org)

javascript - 彻底明白作用域、执行上下文 - 个人文章 - SegmentFault 思否

js
function f1(){
    var n=999;
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 为什么能打印出999,这个值没有被销毁吗?

这段代码演示了 JavaScript 中的闭包(closure)。闭包是 JavaScript 的一个重要概念,它允许函数访问创建时的作用域链,即使该函数在其原始作用域外部被执行。

在这段代码中:

  1. f1 函数定义了一个局部变量 n 并赋值为 999
  2. f1 函数内部,定义了另一个函数 f2f2 函数通过 alert 显示变量 n 的值。
  3. f1 函数通过 return f2 返回了函数 f2 的引用,而不是执行它。
  4. 变量 result 被赋值为 f1 函数的返回值,即 f2 函数的引用。
  5. 当调用 result() 时,实际上是调用了被赋值的 f2 函数。

即使 f1 函数执行完毕后,其局部变量 n 并没有被销毁,因为 f2 函数形成了对 n 的引用,创建了一个闭包。在闭包中,f2 可以访问 f1 的内部状态(即变量 n)。这就是为什么 result() 能够访问并显示 n 的值 999 的原因。

这个特性不是由于 n 的值没有被销毁,而是因为 f2 持续持有对 f1 作用域的引用,这个引用保持了 n 的值,即使 f1 已经执行完毕。这个持续的引用被称为闭包,它允许 f2 访问并操作 f1 的局部变量,即使 f2 是在 f1 外部被调用的。

这种模式在 JavaScript 编程中非常常见,特别是在处理模块、异步回调、和各种设计模式时。

作用域、上下文

js
var a = 1
function out(){
    var a = 2
    inner()
}
function inner(){
    console.log(a)
}
out()  //====>  1,为什么不打印2

在这段代码中,变量 a 在全局作用域中被赋值为 1。然后定义了 out 函数,在其中又局部声明并赋值为 2 的另一个 a。接着定义了 inner 函数,在该函数中打印变量 a 的值。

当调用 out() 函数时,发生了以下步骤:

  1. 执行 out 函数,创建了 out 函数的作用域。
  2. out 函数内部声明了局部变量 a 并赋值为 2
  3. 调用了 inner 函数。

关键点在于 inner 函数。这个函数是在全局作用域中定义的,而不是在 out 函数内。因此,inner 函数没有自己的局部变量 a,它访问的是它自己作用域链中的 a。由于 inner 函数中没有声明 a,它会继续沿着作用域链向上查找,直到全局作用域中找到 a 的声明。

由于在 inner 函数中没有局部变量 a 的声明,它实际上访问的是全局变量 a。所以,console.log(a) 打印的是全局作用域中的 a 的值,即 1,而不是 out 函数中的局部变量 a 的值 2

如果你希望 inner 函数打印 out 函数中局部作用域的 a,你需要在 inner 函数中通过 arguments.callee 访问外部函数的变量(这是一种不推荐的做法,因为 arguments.callee 已经被标记为废弃)。或者,你可以修改 inner 函数,使其接收一个参数,这个参数是它应该打印的 a 的值:

javascript
function inner(a) {
    console.log(a);
}

out(); // 现在会打印 2

在这个修改后的版本中,当调用 out() 时,inner 函数接收 out 函数中局部作用域的 a 的值作为参数,并打印出来,输出结果为 2。但请注意,直接将外部函数的变量传递给内部函数通常是更清晰和更推荐的做法。