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