使用变量时到底发生了什么?

时间:2018-08-05 08:14:24

标签: function variables scope memory-address

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

我注意到,当创建使用外部作用域中的变量的函数(在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

我认为这只能用于对象,因为您可以在函数内修改对象,而不能修改原始变量,因为无法在不重新分配它们的情况下更改它们的值。我想这是一个不同的故事。

我想了解的是:键入变量名时,键入其内存地址是我真正在做什么?所有语言都会发生这种情况吗?

1 个答案:

答案 0 :(得分:3)

  

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

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

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

  

通过地址

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

但是,正如我所说,对于捕获的变量,编译器通常将需要做更多的工作,以使这些变量不仅存储在激活记录中,而且其生存期不再绑定到一个电话。