首页 > 解决方案 > 使用变量时真正发生了什么?

问题描述

我有一个非常基本的问题,关于我迄今为止从未关注过的事情:

我注意到,当创建一个使用外部范围变量的函数(在 JS 或 Python 中)时,该函数不是使用变量的值定义的,而是使用变量本身定义的。因此,如果我更改变量的值,该函数将使用新值。这就是我的意思

let a = 10;
function printA(){
  console.log(a);
}
printA(); // => 10
a = 20;
printA(); // => 20

a = 10
def printA():
  print(a)
printA() # => 10
a = 20
printA() # => 20

我认为这仅适用于对象,因为您可以修改函数内部的对象而不是原始变量,因为如果不重新签名就无法更改它们的值。我想这是一个不同的故事。

我想了解的是:当输入一个变量名时,输入它的内存地址我到底在做什么?所有语言都会出现这种情况吗?

标签: functionvariablesscopememory-address

解决方案


当我创建一个像 printA() 这样使用不是参数的变量的函数时,该变量是否通过其地址永久绑定到该函数?

该变量a被函数“捕获”。发生这种情况的细节通常是实现细节,并且可能导致编译器/解释器生成与原始代码不太相似的代码。

例如,在 C# 中(我知道,不是您提到的语言之一,但它是我最熟悉的语言),编译器将创建一个单独的隐藏类,该类实际上包含由 lambda 捕获的变量的字段或嵌套函数。然后它访问这些字段而不是普通变量。

通过它的地址

变量通常没有 地址。例如,每次调用方法时,它通常都会创建某种类型的“激活记录”,其中通常包含其变量。但请注意,这些记录不在某个固定位置,这就是您可以在不受干扰的情况下并行执行方法、递归等的方式。(一些较旧的 BASIC 确实有固定的激活记录,这就是它们不允许递归的原因)。这些激活记录通常可以放在某种堆栈上。

但正如我所说,对于捕获的变量,编译器通常需要做更多的事情,以便这些变量不只是存储在激活记录中,并且它们的生命周期不再与单个调用绑定。


推荐阅读