Lua Academy · Lektion

__index og __newindex

Opfang læsning og skrivning af felter med metametoderne __index og __newindex.

Lektion 2 af 412 trin

__index og __newindex er en gratis Lua Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Lua Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Lua Academy-kurset indeholder 4 lektioner i alt.

Opsummering af __index

__index udløses, når Lua forsøger at læse en nøgle, der ikke findes i tabellen. Det kan være en tabel (opslag i prototype) eller en funktion (dynamisk beregning). Dette er den mest anvendte metametode i Lua – den understøtter nedarvning, standardværdier og doven initialisering.

local defaults = {timeout=30, retries=3}
local config = setmetatable({timeout=60}, {__index=defaults})

print(config.timeout)   -- 60 (own value)
print(config.retries)   -- 3  (from defaults)
print(config.missing)   -- nil (not in either)

__newindex udløses

__newindex udløses, når Lua forsøger at skrive en nøgle, der endnu ikke findes i tabellen. Hvis nøglen allerede findes, sker tildelingen direkte uden at udløse __newindex.

local proxy = setmetatable({}, {
  __newindex = function(t, k, v)
    print("New key:", k, "=", v)
    rawset(t, k, v)  -- actually store it
  end
})

proxy.x = 10    -- New key: x = 10
proxy.x = 20    -- no trigger! x already exists
print(proxy.x)  -- 20

Skrivebeskyttede tabeller

Brug __newindex til at oprette skrivebeskyttede tabeller. Opfang alle skrivehandlinger, og udløs en fejl. Kombiner det med __index for at levere værdier uden at gemme dem i selve tabellen.

local function readOnly(t)
  return setmetatable({}, {
    __index = t,
    __newindex = function(_, k, _)
      error("attempt to write to read-only table key: " .. tostring(k), 2)
    end
  })
end

local cfg = readOnly({host="localhost", port=8080})
print(cfg.host)    -- localhost
-- cfg.host = "x"  -- ERROR: attempt to write to read-only table

Sporing af ændringer

Brug __newindex til at spore alle ændringer i en tabel – det er nyttigt til fejlfinding, ændringslogning eller reaktive databindinger. Gem data i en skjult tabel, og registrer ændringer i en log.

local log = {}
local data = {}
local tracked = setmetatable({}, {
  __index = data,
  __newindex = function(_, k, v)
    log[#log+1] = {key=k, old=data[k], new=v}
    data[k] = v
  end
})

tracked.name = "Alice"
tracked.age = 30
tracked.name = "Bob"

for _, entry in ipairs(log) do
  print(entry.key, entry.old, "->", entry.new)
end

Proxy-objekter

En proxy omslutter et andet objekt for at opfange alle læsninger og skrivninger. Brug en tom tabel som proxy, hvor både __index og __newindex peger på funktioner. De rigtige data ligger i en separat tabel.

local function makeProxy(target)
  return setmetatable({}, {
    __index = function(_, k) return target[k] end,
    __newindex = function(_, k, v)
      print("Setting", k, "on target")
      target[k] = v
    end,
  })
end

local real = {x=10}
local p = makeProxy(real)
print(p.x)   -- 10 (reads from real)
p.y = 20     -- Setting y on target
print(real.y) -- 20

__index-kæde (nedarvning)

Ved at kæde __index-metatabeller sammen opretter du prototypenedarvning. Når en nøgle ikke findes i objektet, kontrollerer Lua klassen, derefter forældreklassen og så videre. Hvert niveau skal have sin egen metatabel med __index, der peger på det næste niveau.

local Base  = {type = "base",  version = 1}
local Child = setmetatable({type = "child"}, {__index = Base})
local Obj   = setmetatable({}, {__index = Child})

print(Obj.type)     -- base? No: child (Child has it)
print(Obj.version)  -- 1 (from Base)
print(Obj.type)     -- child (Child overrides Base.type)

Doven initialisering med __index

Brug en funktionsform af __index til at beregne og cache dyre værdier ved første adgang. Ved den første læsning beregnes værdien og gemmes direkte i tabellen, så fremtidige kald af __index omgås.

local lazy = setmetatable({}, {
  __index = function(t, k)
    if k == "expensiveData" then
      print("Computing...")
      local result = {1,2,3,4,5}   -- simulate work
      rawset(t, k, result)
      return result
    end
  end
})

print(lazy.expensiveData)  -- Computing...  then table
print(lazy.expensiveData)  -- (no "Computing" this time)

__newindex til skemavalidering

Brug __newindex til at håndhæve et skema: Tillad kun angivelse af foruddefinerede nøgler eller værdier af den korrekte type. Det giver letvægtstypekontrol i Lua-tabeller.

local schema = {name="string", age="number", active="boolean"}

local function schemaTable()
  local store = {}
  return setmetatable({}, {
    __index = store,
    __newindex = function(_, k, v)
      local expected = schema[k]
      if not expected then error("unknown key: "..k,2) end
      if type(v) ~= expected then
        error(k.." must be "..expected..", got "..type(v),2)
      end
      store[k] = v
    end
  })
end

local user = schemaTable()
user.name = "Alice"
user.age  = 30
-- user.age = "thirty"  -- ERROR

__index kontra rawget

Brug rawget inde i en __index-funktion til at læse fra tabellen uden at udløse __index igen, hvilket ellers ville medføre uendelig rekursion. Brug altid rawget/rawset inde i metametoder til at manipulere den faktiske lagerplads i tabellen.

local counter = setmetatable({count=0}, {
  __index = function(t, k)
    -- Use rawget to avoid recursion
    local c = rawget(t, "count")
    rawset(t, "count", c + 1)
    return rawget(t, k)
  end
})

print(counter.count)    -- 0 (direct, no __index)
print(counter.missing)  -- nil
print(counter.count)    -- 1 (incremented)

Kombination af __index og __newindex

Det mest kraftfulde mønster er at bruge en tom proxy-tabel med både __index og __newindex, understøttet af en skjult datatabel. Det giver fuld kontrol over læsninger og skrivninger og muliggør funktioner som ændringsregistrering, doven indlæsning og adgangskontrol.

local function observable(init)
  local data = init or {}
  local listeners = {}
  local obj = setmetatable({}, {
    __index = data,
    __newindex = function(_, k, v)
      local old = data[k]
      data[k] = v
      for _, cb in ipairs(listeners) do cb(k,old,v) end
    end
  })
  obj._onchange = function(_, cb) listeners[#listeners+1]=cb end
  return obj
end

local obs = observable({score=0})
obs:_onchange(function(k,o,n) print(k,o,"->",n) end)
obs.score = 100  -- score  0 -> 100

Hurtigt tjek

Hvornår udløses __newindex IKKE?

Opsummering: __index og __newindex

Vigtige punkter:

  • __index: udløses ved læsning af en manglende nøgle; tabel eller funktion
  • __newindex: udløses kun ved skrivning af en ny nøgle
  • Brug rawget/rawset inde i metametoder for at undgå rekursion
  • Mønstre: skrivebeskyttede tabeller, ændringssporing, doven initialisering, skemavalidering
  • Tom proxy + understøttende tabel = fuld opfangning af læsninger og skrivninger
Gratis at komme i gang

Lær Lua med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
40
Lektioner
159

Ofte stillede spørgsmål

Er lektionen “__index og __newindex” gratis?

Ja — alle 3 lektioner i læringssporet Lua Academy, inklusive “__index og __newindex”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Lua Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “__index og __newindex”?

Opfang læsning og skrivning af felter med metametoderne __index og __newindex. Du øver dig i Lua Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Lua Academy?

Der kræves ingen tidligere erfaring. Lua Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “__index og __newindex”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Lua Academy-lektion?

Ja. Alle Lua Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Introduktion til metatabeller
  2. __index og __newindex
  3. Aritmetiske metametoder
  4. __tostring og __len
← Tilbage til Lua Academy