_ENV和_G
5.1之前, 全局變量存儲在_G這個table中, 這樣的操作:
a = 1?
相當于:
_G['a'] = 1
但在5.2之后, 引入了_ENV叫做環境,與_G全局變量表產生了一些混淆,需要從原理上做一個理解。
在5.2中,?
操作a = 1
相當于
_ENV['a'] = 1
這是一個最基礎的認知改變,其次要格外注意_ENV不是全局變量,而是一個upvalue(非局部變量)。
其次,_ENV['_G']指向了_ENV自身,這一目的是為了兼容5.1之前的版本,因為之前你也許會用到:
_G['a'] = 2 , 在5.2中, 這相當于_ENV['_G']['a'],為了避免5.1之前的老代碼在5.2中運行錯誤,所以5.2設置了_ENV['_G']=_ENV來兼容這個問題。然而你不要忘記_ENV['_G']=_ENV,所以一切都順理成章了。
在5.1中,我們可以為一段代碼塊(或者函數)設置環境,使用函數setfuncs,這樣會導致那一段代碼/函數訪問全局變量的時候使用了setfuncs指定的table,而不是全局的_G。
在5.2中,setfuncs遭到了廢棄,因為引入了_ENV。 通過在函數定義前覆蓋_ENV變量即可為函數定義設置一個全新的環境,比如:
a = 3
function get_echo()
local _ENV={print=print, a = 2}
return function echo()
print(a)
end
end
get_echo()()
會打印2,而不是3,因為echo函數的環境被修改為{print=print, a=2},而print(a)相當于訪問_ENV['a'](先忘掉那為了兼容而存在的_G)。
這就是_ENV的基本用法了。
另外,不得不提到lua的C支持中關于全局變量與環境的細節,只能簡單描述,你必須自己試試才能記得清楚。
lua_setglobal/lua_getglobal都是操作lua_State注冊表中LUA_RIDX_GLOBALS偽索引指向的全局變量表,與lua中訪問_ENV['a']或者a是不同的。
lua_load加載lua代碼后會返回一個函數,默認會給這個函數設置一個upvalue就叫_ENV,起值是LUA_RIDX_GLOBALS的全局變量表,你可以lua_setupvalue設置這個函數的upvalue,即下標1的upvalue,因為這個位置是這個函數的_ENV表存放位置(你可以通過lua_setupvalue的返回值印證這一點)
這里巧妙的是,lua_State會在創建時保證LUA_RIDX_GLOBALS的全局變量表中包含一個指向自己的_G元素,這樣就保證了在不調用lua_setupvalue的情況下該返回函數的_ENV['_G']是指向自己的,即LUA_RIDX_GLOBALS這個全局表。(其實你的lua解釋器就是簡單的lua_load后pcall的,對于一個剛啟動lua_State來說是沒有_ENV的,是lua解釋器load你的代碼時自動給帶上的_ENV,其值是lua_state的LUA_RIDX_GLOBALS全局表。)
一些有意思的東西是需要你自己摸索的,lua語言自身就很簡練,并且所有東西都不是什么神秘的事情,可以通過讀源碼或者試驗摸索得到。
最后,提一下,lua_state啟動后在注冊表里LUA_RIDX_GLOBALS下標存放的全局表一定有一個元素是指向自己的,即_G.
?
?
任何的全局變量名 var 在語法分析的時候都會被翻譯為 _ENV .var的形式,這個會在 3.2 和 3.3.3 時再討論。每一個代碼塊在它的作用域內都有一個叫 _ENV 的外部局部變量(參考
3.3.2),所以_ENV 在代碼塊里也不是一個全局變量。
雖然_ENV 看起來像是編譯器生成的內容,但它確是實實在在存在的。你可以直接 在
Lua 的代碼中使用_ENV 來定義一個新的變量和參數。每一個通過 _ENV 引用的全局名字, 它在程序中是被全局可見的,它遵守 Lua 的可見性規則(參考 3.5)。
任何一個用法如_ENV 一樣的 table 都稱為環境。
Lua 中保留了一個特殊的全局環境 。它的值被保留在 Lua 棧中,通過一個特殊的索引值 來訪問(參考 4.5)。在 Lua 的代碼中,變量_G 指向這個值。
當編譯一個代碼塊時 ,Lua 會把全局環境中的內容作為數據來初始化 _ENV(參考 load)。 因此,Lua 代碼中的全局變量指的是全局環境中的內容 。此外,所有標準庫中的函數都會被 加載進全局環境中,同時也提供了一些操作這個環境的函數 。你可以使用 load(或者 loadfile) 在加載一個代碼塊時指定不同的環境 。(在 C 代碼中,你必須在加載代碼塊后通過改變第一 個上值(upvalue)來實現環境的改變 。)
如果你改變了 Lua 注冊表中的全局環境(通過 C 代碼或調試庫),那么之后加載的代碼 塊將會得到一個新的環境。之前加載的代碼塊不受影響,它仍然使用之前的 _ENV 變量。此 外,變量_G(儲存在原先的全局環境中)從來不會被 Lua 更新?
?
?
1._ENV是一個外部全局變量
2.
?
總結
- 上一篇: 【Android UI设计与开发】3.引
- 下一篇: 超级组合:用户中心+云平台