不,不完全是。命名空间和调用框架是非常不同的概念。命名空间是名称的层次结构,可以消除同义词的歧义。您的程序中可能有三个名为 foo 的变量,但如果您将它们放在不同的命名空间中,它们不会发生冲突。命名空间可用于变量名和命令名。一旦使用namespace eval 创建命名空间的内容,在您调用namespace delete 之前,它始终是可访问的。
调用堆栈是一系列堆栈帧。第一个堆栈帧 #0 始终存在。每当调用命令时都会创建其他堆栈帧(这主要用于作为用户定义过程的命令,“内置”命令遵循它们自己的规则)。当命令返回时,它们再次被销毁。因此,如果你调用命令 A,A 调用命令 B,B 调用命令 C,你的调用堆栈如下所示:
#3 : <C's variables>
#2 : <B's variables>
#1 : <A's variables>
#0 : <global and namespace variables>
从某种意义上说,每个堆栈帧都是一个范围,只有在那里创建或导入其中的变量才能被访问,除非您使用upvar。其他一切都是隐藏的。在大多数编程语言中,可以从内部范围自动访问外部范围(例如全局范围)的名称。在 Tcl 中并非如此。
使用upvar,您可以让命令查看它自己的堆栈框架之外的内容。例如,C 可以使用 upvar #0 foo bar 为全局变量 foo 创建别名 (bar),或使用 upvar 1 baz qux(注意不带 #)为变量创建别名 (qux) baz 在 B 的栈帧中。
uplevel 命令可以按照相同的方式在另一个堆栈帧中执行脚本,包括全局堆栈帧。在执行期间,脚本可以访问该堆栈帧中的所有内容,但不能访问其他任何内容,包括调用 uplevel 的堆栈帧中的变量。
C 也可以使用upvar #0 ::abc::def ghi 为命名空间变量::abc::def 创建别名,但不要这样做,而是使用namespace upvar ::abc def ghi。
您可以使用global foo 代替upvar #0 foo foo 来导入全局变量。在命名空间中定义的命令内部,variable 命令可以导入在同一命名空间中定义的变量。
将upvar 或uplevel 放入#0(全局框架)或1(调用者的框架)通常很有用。使用其他帧号很容易出错,通常表示设计不佳。调用 upvar 0 foo bar 为同一堆栈帧中的变量 (foo) 创建别名 (bar),这可能非常有用。
由正在处理的事件调用的命令在调用堆栈之外使用全局级别执行。他们无法进入活动堆栈帧内部并访问驻留在其中的变量。
一个简单的演示:
namespace eval ::abc {
variable def 42
proc xyz {} {
variable def
}
}
set foo 1138
proc A {} {
B
}
proc B {} {
set baz 1337
C
}
proc C {} {
upvar #0 foo bar
puts $bar
upvar 1 baz qux
puts $qux
namespace upvar ::abc def ghi
puts $ghi
}