環境教學手冊 |
|
預設全域表格儲存在 "_G"
鍵值中,如果你想要取得它實際的 ref,這將會很有用。
Lua 5.2 中環境運作的方式與 5.1 非常不同。這兩種方式將會在這裡解說說明。
函式的環境儲存在一個命名為 _ENV
的 upvalue 中。身為一個範例,這裡有一個函式將環境設定為自訂環境,並使用其中的變數
print(_ENV == _G) -- prints true, since the default _ENV is set to the global table a = 1 local function f(t) local print = print -- since we will change the environment, standard functions will not be visible local _ENV = t -- change the environment. without the local, this would change the environment for the entire chunk print(getmetatable) -- prints nil, since global variables (including the standard functions) are not in the new env a = 2 -- create a new entry in t, doesn't touch the original "a" global b = 3 -- create a new entry in t end local t = {} f(t) print(a, b) --> 1 nil print(t.a, t.b) --> 2 3
載入區塊時,頂層函式會取得一個新的 _ENV upvalue,且任何巢狀函式都能看到它。你可以想像載入運作類似這樣
local _ENV = _G return function (...) -- this function is what's returned from load -- code you passed to load goes here, with all global variable names replaced with _ENV lookups -- so, for example "a = b" becomes "_ENV.a = _ENV.b" if neither a nor b were declared local end
在大部分情況下,你不必使用環境,除非你想對所載入的區塊實施沙盒,方便存取某些函式 (透過讓它們看起來像是全域的) 或為了安全因素讓它看不到不安全的函式。這就是為什麼 5.2 的 load 函式會將參數設定為允許你設定區塊的 _ENV 成為自訂表格,而非 _G。
local sandbox_env = { print = print, } local chunk = load("print('inside sandbox'); os.execute('echo unsafe')", "sandbox string", "bt", sandbox_env) chunk() -- prevents os.execute from being called, instead raises an error saying that os is nil
如果你實際上想建立一個沙盒來執行不可信的程式碼,請記得,很容易會忽略很多能被利用的東西,以及你會需要一些限制 CPU 使用率和記憶體的方法。
在 Lua 5.1 中,環境有自己的形式,與區域變數或 upvalue 無關。反之,每個函式有一個與它有關的環境表格,可使用標準函式 getfenv
/setfenv
來處理。
getfenv
和 setfenv
兩者都接受函式或堆疊層級 (其中 1 是目前函式,2 是呼叫目前函式的函式,依此類推)。setfenv
有第二個參數,用來接受新的 env 表格,而 getfenv
會回傳函式的當前 env 表格。
先前範例改寫為 5.1
print(getfenv(1) == _G) -- prints true, since the default env is set to the global table a = 1 local function f(t) local print = print -- since we will change the environment, standard functions will not be visible setfenv(1, t) -- change the environment print(getmetatable) -- prints nil, since global variables (including the standard functions) are not in the new env a = 2 -- create a new entry in t, doesn't touch the original "a" global b = 3 -- create a new entry in t end local t = {} f(t) print(a, b) --> 1 nil print(t.a, t.b) --> 2 3
而沙盒範例改寫為 5.1
local sandbox_env = { print = print, } local chunk = loadstring("print('inside sandbox'); os.execute('echo unsafe')") setfenv(chunk, sandbox_env) chunk() -- prevents os.execute from being called, instead raises an error saying that os is nil
有時候會覺得 5.1 的方式比較簡單且用途較廣,但它也需要對環境進行個別處理 (而非使用現有的區域變數系統)。另外,5.2 的方式是設計成在沒有 debug 函式庫的情況下,從各地存取函式的環境,因此可以被認為較為安全。