Scope(作用域)

Time:5 分钟

进行脚本编写时,开发者可以通过 Scope(作用域)对变量或函数的“可见”和可访问区域进行定义。articles/Loops|循环articles/Function|函数articles/Conditional Statements in Lua|条件语句等元素都会创建新的 Scope Block(作用域区块)。每个区块都能够访问处于其父项区块中的剧本变量或函数,但当这些数据位于其子项区块时将无法对其进行获取。

https://developer.roblox.com/assets/bltd46ee264544ef2f3/Scope-Diagram.png

https://developer.roblox.com/assets/blt0ae9a4266bb09810/Green-Bullet.png

B 区块可以访问 A 区块中的局部变量。

https://developer.roblox.com/assets/blt0ae9a4266bb09810/Green-Bullet.png

C 区块可以访问 A 区块与 B 区块中的局部函数与变量。

https://developer.roblox.com/assets/blt4250e428bdfc7e26/Red-Bullet.png

A 区块无法访问 B 区块与 C 区块中的局部函数与变量。

https://developer.roblox.com/assets/blt4250e428bdfc7e26/Red-Bullet.png

B 区块无法访问 C 区块中的局部变量。

Local Scope(局部作用域)

在变量与函数前添加 local 前缀后,可以将其作用域限制为局部作用域。下列示例中的 testFunc() 函数和 testVar 变量的作用域均已被设为局部:

Local Scope(局部作用域) ```

local function testFunc()  -- 作用域为局部
	local testVar = 64  -- 作用域为局部
	print(testVar)
end

testFunc()

print(testVar)


64
nil
此示例中 `testVar` 的作用域限制为 `testFunc()` 函数内(其访问范围受到了同样限制),因此在第 3 行对其进行输出时将会按照预期产生 `64` 。但在第 8 行尝试输出该变量时则会产生 `nil`:这是因为在离开特定作用域后,在该作用域级别下创建的变量会被弃用,也就是被“遗忘”了。

## Global Scope(全局作用域)

对全局变量和函数进行声明后,就可以在同一脚本声明后的任意代码块中对其进行使用。变量和函数的作用域默认为全局,除非开发者特意用 `local` 关键字对其进行标记。

下列示例中的 `testVar` 在局部函数 `testFunc()` 函数中进行了全局声明。调用该函数后,`testVar` 被设置为 `64` 。由于该变量为全局作用域并可以在函数外进行访问,因此对其在第 7 行进行输出时同样按预期输出了 `64`。

Global Scope(全局作用域) ```    
    
    local function testFunc()  -- 作用域为局部
    	testVar = 64  -- 作用域为全局
    end
    
    testFunc()
    
    print(testVar)
    
    
    64

全局注意事项

Although it may seem convenient to use global scope throughout a script, note the following:

  • Because global variables and functions must be accessed by a hash lookup, they can be expensive to use in terms of performance. In fact, a global variable accessed in a time-critical loop can perform 10% slower (or worse) than a local variable in the same loop.

  • As noted earlier, global variables and functions are only accessible within the associated script, not between multiple scripts. Thus, a global variable/function doesn’t provide any benefit over an in-scope local equivalent or a forward declaration.

作用域的实用实践

Forward Declaration(前向声明)

在需要时,开发者可以对局部变量进行定义而不为其进行赋值。进行该操作后,可以在其作用域范围子项作用域级别中对该变量进行读取。该操作通常被称为前向声明Upvalue(外部局部变量)。

下列示例的第 8 行对局部变量 fruitName 进行了前向声明。在其后的 for 循环中(该循环的子项作用域区块)对 fruitName 分配了一个值,且该值在循环后被返回。整个 getFruitByColor() 函数中的 fruitName 一直都是同一个局部变量,此示例中的前向声明避免了当对 fruitName 进行全局声明时可能会出现的冲突或覆盖情况。

Forward Declaration(前向声明) ```

local fruitTable = {
	Lemon = "Yellow",
	Apple = "Red",
	Orange = "Orange"
}

local function getFruitByColor(color)
	local fruitName
	for key, value in pairs(fruitTable) do
		if value == color then
			fruitName = key
		end
	end
	return fruitName
end

local fruit = getFruitByColor("Yellow")
print(fruit)


Lemon
### 多脚本数据模块

当需要创建可供多个脚本访问的“全局数据表”时,开发者可以首先在 `ReplicatedStorage` 等其他脚本可以访问的位置创建一个 `ModuleScript` ;然后在 `local module = {}` 和 `return module` 之间创建适配数据的组织表以及子表:

数据模块 ```    
    
    local module = {}
    
    module.VehicleData = {
    	-- 火箭滑车
    	RocketSled = {
    		displayName = "Rocket Sled",
    		topSpeed = 120
    	},
    	-- 蜘蛛坦克
    	SpiderTank = {
    		displayName = "Spider Tank",
    		topSpeed = 40
    	},
    }
    
    return module

接下来在每个需要访问该数据的其他脚本中对模块进行 require(),如下:

    
    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    
    -- 对模块进行 Require
    local DataModule = require(ReplicatedStorage:WaitForChild("DataModule"))

完成之后,即可通过简单操作读取表中的值了:

    
    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    
    -- 对模块进行 Require
    local DataModule = require(ReplicatedStorage:WaitForChild("DataModule"))
    
    local vehicleData = DataModule.VehicleData.RocketSled
    
    local displayName = vehicleData.displayName
    local topSpeed = vehicleData.topSpeed

***Roblox官方链接:Scope(作用域)