初学者适用范围

作为一个 Lua/Solar2D 新手(或相对较新的)程序员,您是否对类似这样的控制台错误感到困惑?

Runtime error /Users/yourname/Projects/AwesomeGame\menu.lua:4: attempt to index global 'button' (a nil value)
stack traceback:
    [C]: in function 'error'
    ?: in function 'gotoScene'
    /Users/yourname/Projects/AwesomeGame\main.lua:16: in main chunk

如果是这样,那么您需要学习一个非常重要的编程概念,称为作用域。作用域会影响 Lua“看到”变量的能力(如上面的错误所示)以及您管理函数相对于其他函数和变量的位置的能力。

作用域定义

当您想到“望远镜”或“显微镜”之类的术语时,您会想到可以帮助您观察事物的设备,并且在一定范围内观察事物——例如,显微镜中的微小物体。在编程术语中,作用域用于定义代码的哪些部分可以“看到”您创建的变量、对象和函数。在 Lua 中,有两个主要的作用域控制:全局局部

全局

全局变量/函数可以在程序中的任何位置被看到。“太好了,我将对所有内容使用全局作用域!”您可能会这么说,但全局对象实际上可能非常糟糕!事实上,新手程序员应该避免使用全局变量

全局变量危险的原因之一是——由于它们没有可见性限制——它们在性能方面使用成本可能很高。事实上,如果在时间紧迫的循环中访问全局变量,它的性能可能比同一循环中的局部变量差 30%。

全局变量/函数还具有被意外重新赋值的固有风险。例如,如果您在一个 Lua 文件中创建名为 `_W` 的全局变量并将其赋值为 `320`,您可能会意外地在另一个文件中创建另一个全局变量 `_W`,并为其赋予不同的值,从而有效地“破坏”了第一个声明。这显然会在第一个文件中产生意想不到的后果,因为它预期 `_W` 的值为 `320`!

告别全局变量!教程更详细地讨论了全局作用域,以及创建/访问伪全局变量和函数的便捷方法。

局部

在 Lua 中,首选作用域是局部作用域,您可以通过在定义变量/函数之前使用 `local` 声明来控制它。例如

local someVariable
local function someFunction()

end

当您在第一次创建变量或函数时在其前面加上 `local` 前缀时,它将对该代码块和其中包含的任何“子”代码块可用。以下两个示例突出了此概念

  1. 考虑这种情况
local function addTwoNumbers( number1, number2 )

    -- Create variable "sum" local only to this "addTwoNumbers()" function
    local sum = number1 + number2
    print( sum )  -- This works!
end

print( sum )  -- This does NOT work (prints "nil" meaning it's unknown to Lua)

在这种情况下,函数 `addTwoNumbers()` 定义了一个新的代码。在此块内部,我们创建了一个名为 `sum` 的新变量。此变量带有 `local` 关键字,这意味着它仅对函数 `addTwoNumbers()` 可见——此模块或整个项目中的任何其他代码都无法“看到”`sum` 变量。因此,当函数结束时,不再存在名为 `sum` 的变量,并且 `nil` 将被打印到控制台。

  1. 现在考虑这个块
local function addTwoNumbers( number1, number2 )

    local sum = number1 + number2
    if sum < 10 then
        print( sum .. " is less than 10" )
        local secondSum = sum + 10
    end

    print( secondSum )  -- This does NOT work (prints "nil" meaning it's unknown to Lua)
end

与第一个示例一样,`sum` 是 `addTwoNumbers()` 的局部变量。这让我们可以将两个数字相加,并且它在由if sum < 10 then启动的块内部也可见。但是,在该if-then块内部,创建了另一个局部变量 (`secondSum`),并且该变量对该if-then块是局部的。因此,尝试在if-then外部打印 `secondSum` 会导致 `nil`,因为 `secondSum` 在执行的该点不存在。

循环中的索引变量

考虑这样的代码块

local i = 999

for i = 1, 10 do
    print( i )  -- prints 1, 2, 3, .. 10
end

print( i )  -- prints 999

这里,在第 1 行将局部变量 `i` 设置为 `999`。此外,使用了循环索引变量 `i`(第 3 行),但此 `i`对 `for` 循环是局部的。当循环执行时,它的值表示1-10并且这反映在第 4 行的print( i )命令中。当循环结束时,此 `i` 将忘记它在循环中的最终值并返回到其先前的定义(通常为 `nil`)。之后,在 `for` 循环之前声明的局部变量 `i` 将被打印到控制台(第 7 行的 `print( i )`),值为 `999`。这是因为 for 循环——就像函数一样——是块,并且它们遵循与块相关的相同作用域规则。

总结

希望本教程已经说明,作用域作为一个整体概念相对简单。当您使用关键字 `local` 时,只能在该块级别或任何子块内访问该变量。考虑到这一点,如果您的变量或函数需要在更广泛的级别上可见,只需将它们移动到块上方/外部或在模块顶部附近定义它们。