Lua局部变量和作用域详解:代码块中的变量管理 | 南锋

南锋

南奔万里空,脱死锋镝余

Lua局部变量和作用域详解:代码块中的变量管理

Lua语言中的变量在默认情况下是全局变量,所有的局部变量在使用前必须声明。与全局变量不同,局部变量的生效范围仅限于声明它的代码块。一个代码块是一个控制结构的主体,或是一个函数的主体,或是一个代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
x = 10 
local i = 1 -- 对于代码段来说是局部的
while i <= x do
local x = i * 2 --对于循环来说是局部的
print(x) -- 2,4,6...
i = i + 1
end
if i > 20 then
local x -- 对于"zhen"来说是局部的
x = 20
print(x + 2) -- 如果测试成功会输出22
else
print(x) -- 10(全局的)
end
print(x)

请注意,上述示例在交互模式中不能正常运行。因为在交互模式中,每一行代码就是一个代码段。一旦输入示例的第二行,Lua语言解释器就会直接运行它并在下一行开始一个新的代码段。这样,局部的声明就超出了原来的作用范围。解决这个问题的一种方式是显示地声明整个代码块,即将它放入一对do-end中。一旦输入了do,命令就只会在遇到匹配的end时才结束,这样Lua语言解释器就不会单独执行每一行的命令。
当需要更好地控制某些局部变量的生效范围时,do程序块也同样有用:

1
2
3
4
5
6
7
8
local x1 , x2
do
local a2 = 2 * a2
local d = (b^2 - 4*a*c)^(1/2)
x1 = (-b + d)/a2
x2 = (-b - d)/a2
end
print(x1,x2)

尽可能地使用局部变量是一种良好的编程风格。首先,局部变量可以避免由于不必要的命名而造成全局变量的混乱;其次,局部变量还能避免同一程序中不同代码部分中的命名冲突;再次,访问局部变量比访问全局变量更快;最后,局部变量会随着其作用域的结束儿消失,从而使得垃圾收集器能够将其释放。
鉴于局部变量优于全局变量,有些人就认为Lua语言应该把变量默认视为局部的。然而,把变量默认视为局部的也有一些列的问题。一个更好的解决办法并不是把变量默认视为局部变量,而是在使用变量前必须先声明。Lua语言的发型版中有一个用于全局变量检查的模块strict.lua,如果视图在一个函数中对不存在的全局变量赋值或者使用不存在的全局变量,将会抛出异常。
局部变量的声明可以包含初始值,其赋值规则与常见的多重赋值一样:多余的值被丢弃,多余的变量被赋值为nil。如果一个声明中没有赋值,则变量会被初始化为nil:

1
2
3
4
5
6
7
local a , b = 1 , 10
if a < b then
print(a) -- 1
local a -- '= nil' 是隐式的
print(a) -- nil
end
print(a,b) -- 1, 10

Lua语言中有一种常见的用法:
local foo = foo
这段代码声明了一个局部变量foo,然后用全局变量foo对其赋值。这个用法在需要提高对foo的访问速度时很有用。当其他函数改变了全局变量foo的值,而代码段又需要保留foo的原始值时,这个用法也很有用,尤其是在进行运行时动态替换时。即使其他代码把print动态替换成了而其他函数,在local print = print语句之前的所有代码使用的还都是原先的print函数。
有些人认为在代码块的中间位置声明变量时一个不好的习惯,实际上恰恰相反,我们很少会在不赋初始值的情况下声明变量,在需要时才声明变量可以避免漏掉初始化这个变量。此外,通过缩小变量的作用域还有助于提高代码的可读性。

+