【发布时间】:2020-11-29 21:11:46
【问题描述】:
以下 Python 程序 A 按预期输出 1,而以下 Python 程序 B 引发未绑定局部变量 x 错误,违反直觉。
- 程序 A:
def f(): print(x)
x = 1
f()
- 程序 B:
def f(): print(x); x = 2
x = 1
f()
Javascript 具有完全相同的行为。
- 程序 A:
function f() { console.log(x); }
let x = 1;
f();
- 程序 B:
function f() { console.log(x); let x = 2; }
let x = 1;
f();
但是,C++ 在这两种情况下都会按预期输出1。
- 程序 A:
#include <iostream>
int x;
void f() { std::cout << x; }
int main() { x = 1; f(); return 0; }
- 程序 B:
#include <iostream>
int x;
void f() { std::cout << x; int x = 2; }
int main() { x = 1; f(); return 0; }
所以所有程序A输出1。一方面,Python 和 Javascript 与 C++ 之间的程序 B 的差异是由于它们不同的作用域规则:在 C++ 中,变量的作用域从它的声明开始,而在 Python 和 Javascript 中,它从声明变量的块的开头开始。因此,在 C++ 中,函数 f 中的打印变量 x 解析为全局变量 x 的值 1,因为它是执行时上下文中唯一的变量。在 Python 和 Javascript 中,函数 f 中的打印变量 x 解析为空并引发未绑定的局部变量 x 错误,因为局部变量 x 在执行时已经在上下文中,因此它掩盖了全局变量 @987654341 @ 尚未绑定到值 2。 Python 和 Javascript 的这种违反直觉的行为也称为变量提升,因为它在块的开头“提升”变量声明(但不是定义)。
编程语言中变量提升的优缺点是什么?
【问题讨论】:
-
“违反直觉的行为”是由于块/函数范围。在内部,当解析嵌套作用域中的变量时,可以更快地从其作用域的顶部找到声明。最初在 JS 中(解释为
var和函数范围),您可以运行“愚蠢”的代码而不会中断执行。缺点是难以调试的逻辑错误。 -
@Teemu 是的
var不会像let那样中断执行,因为它将值undefined分配给局部变量x而不是什么都没有,而是局部变量x的范围保持不变,因为它仍然像let一样屏蔽全局变量x。 -
是的,这是提升的结果。在
let(和const)的上下文中,使用了术语temporal dead-zone。 -
Guido van Rossum(Python 的创造者)自己回答了here。
标签: javascript python c++ scope hoisting