Выдержка из руководства по Lua 5.3:
_G
Глобальная переменная (не функция), которая содержит глобальную среду (см. §2.2). Сам Lua не использует эту переменную; изменение его значения не влияет ни на какую среду, и наоборот.
Соответствующая часть из §2.2
[…] Каждый чанк компилируется в области видимости внешней локальной переменной с именем
_ENV
, так что_ENV
сам по себе никогда не является свободным именем в чанке.[…]
Любая таблица, используемая в качестве значения
_ENV
, называется средой.Lua поддерживает особую среду, называемую глобальной средой. Это значение хранится в специальном индексе в реестре C. В Lua глобальная переменная
_G
инициализируется этим же значением. (_G
никогда не используется внутри компании.)Когда Lua загружает чанк, значением по умолчанию для его
_ENV
upvalue является глобальная среда. Поэтому по умолчанию свободные имена в коде Lua относятся к записям в глобальной среде.
Я понимаю, что для каждого загруженного фрагмента, поскольку _ENV
будет первым повышающим значением, он указывает на глобальную таблицу среды, на которую указывает _G
load
.
> =_G, _ENV
table: 006d1bd8 table: 006d1bd8
подтверждает, что оба указывают на одну и ту же таблицу. В руководстве говорится, а несколько раз заверяется, что _ENV
и _G
- это просто обычные имена без скрытого значения и что сам Lua не использует их для внутренних целей. Я попробовал этот фрагмент ниже:
local a = { }
local b = a -- since tables are objects, both refer to the same table object
print(a, b) -- same address printed twice
a = { } -- point one of them to a newly constructed table
print(a, b) -- new, old table addresses printed
Теперь проделаем то же самое с _G
и _ENV
:
local g = _G -- make an additional reference
print(g, _G, _ENV) -- prints same address thrice
local p = print -- backup print for later use
_ENV = { } -- point _ENV to a new table/environment
p(g, _G, _ENV) -- old, nil, new
table: 00ce1be0 table: 00ce1be0 table: 00ce1be0
table: 00ce1be0 nil table: 00ce96e0
Если _G
- обычный глобал, почему он становится nil
здесь? Если подсчет ссылок выполнен, _G
все еще удерживал ссылку в то время, когда _ENV
ее выпустил. Как и b
выше, он тоже должен держаться за старую таблицу, не так ли?
Однако для приведенного ниже фрагмента _G
не изменился / сохранен!
_ENV = { _G = _G }
_G.print(_G, _ENV, _ENV._G) -- old, new, old
Но здесь он убит:
_ENV = { g = _G }
_ENV.g.print(_ENV, _ENV.g, _G) -- new, old, nil
Другой случай, когда он сохранился:
print(_G, _ENV) -- print same address twice
local newgt = {} -- create new environment
setmetatable(newgt, {__index = _G}) -- set metatable with _G as __index metamethod
_ENV = newgt -- point _ENV to newgt
print(_G, newgt, _ENV) -- old, new, new
При таком большом количестве вариаций в поведении _G
первоначальное заверение, данное в руководстве, кажется шатким. Что мне здесь не хватает?
_ENV.
. Таким образом, любое вхождение_G
становится_ENV._G
перед выполнением вашего скрипта. - person Egor Skriptunoff   schedule 10.03.2016