Lua Academy · บทเรียน

รู้จักเมทาเทเบิล

กำหนดและอ่านเมทาเทเบิลด้วย setmetatable/getmetatable

บทเรียน 1 จาก 412 ขั้นตอน

รู้จักเมทาเทเบิล เป็นบทเรียน Lua Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Lua Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน

Metatable คืออะไร

metatable คือตาราง Lua ปกติที่ควบคุมพฤติกรรมของตารางอีกตัวหนึ่ง เมื่อ Lua พบการดำเนินการที่ไม่สามารถทำกับตารางได้ เช่น การคำนวณทางคณิตศาสตร์หรือการเข้าถึงคีย์ที่ไม่มีอยู่ ระบบจะค้นหา เมทาเมธอด ที่สอดคล้องกันในเมตาเทเบิล นี่คือกลไกการขยายความสามารถของ Lua สำหรับการโอเวอร์โหลดตัวดำเนินการและการเขียนโปรแกรมเชิงวัตถุ

local t = {}
local mt = {__index = function(_, k) return "default_" .. k end}

setmetatable(t, mt)

print(t.foo)    -- default_foo
print(t.bar)    -- default_bar
print(t[1])     -- default_1

setmetatable และ getmetatable

setmetatable(t, mt) กำหนดเมตาเทเบิลของ t ให้เป็น mt และคืนค่า t ส่วน getmetatable(t) จะคืนค่าเมตาเทเบิล หรือ nil หากไม่มี คุณสามารถป้องกันไม่ให้มีการเปลี่ยนแปลงเมตาเทเบิลได้ด้วยการกำหนด mt.__metatable = "protected"

local t = {x = 10}
local mt = {}

setmetatable(t, mt)
print(getmetatable(t) == mt)   -- true

-- Protect metatable
mt.__metatable = "locked"
print(getmetatable(t))   -- locked
-- setmetatable(t, {})  -- ERROR: cannot change protected metatable

__index ในรูปแบบตาราง

เมื่อ __index เป็นตาราง Lua จะค้นหาคีย์ที่ไม่มีอยู่ในตารางนั้น นี่เป็นพื้นฐานของการสืบทอดต้นแบบใน Lua หากไม่มีคีย์ดังกล่าวใน __index เช่นกัน Lua จะตรวจสอบเมตาเทเบิลของตารางนั้นต่อไปตามลำดับชั้น

local Animal = {sound = "...", legs = 4}

local Dog = setmetatable({sound = "Woof"}, {__index = Animal})
local Cat = setmetatable({sound = "Meow"}, {__index = Animal})

print(Dog.sound)   -- Woof (own field)
print(Dog.legs)    -- 4 (from Animal)
print(Cat.sound)   -- Meow
print(Cat.legs)    -- 4

__index ในรูปแบบฟังก์ชัน

เมื่อ __index เป็นฟังก์ชัน Lua จะเรียกฟังก์ชันนั้นพร้อมตารางและคีย์ที่ไม่มีอยู่ ฟังก์ชันสามารถคำนวณหรือดึงค่าแบบไดนามิกได้ ให้คืนค่า nil จาก __index เพื่อระบุว่า "ไม่พบคีย์"

local dynamic = setmetatable({}, {
  __index = function(t, k)
    if k:sub(1,4) == "get_" then
      local field = k:sub(5)
      return function(self) return self[field] end
    end
    return nil
  end
})

dynamic.name = "Lua"
local getter = dynamic.get_name
print(getter(dynamic))   -- Lua

rawget และ rawset

rawget(t, k) อ่านคีย์ของตารางโดยไม่เรียกใช้ __index ส่วน rawset(t, k, v) กำหนดคีย์โดยไม่เรียกใช้ __newindex ใช้ฟังก์ชันเหล่านี้เพื่อข้ามเมทาเมธอดเมื่อต้องการเข้าถึงตารางโดยตรงภายในเมทาเมธอด และหลีกเลี่ยงการเรียกซ้ำไม่รู้จบ

local logged = setmetatable({}, {
  __newindex = function(t, k, v)
    print("Setting", k, "=", v)
    rawset(t, k, v)  -- bypass __newindex
  end
})

logged.x = 10   -- Setting x = 10
logged.x = 20   -- no log! key now exists in t
print(logged.x) -- 20

หลายตารางใช้เมตาเทเบิลร่วมกัน

เมตาเทเบิลหนึ่งรายการสามารถใช้ร่วมกันโดยหลายตารางได้ นี่คือหลักการทำงานของระบบคลาสใน Lua โดยอินสแตนซ์ทั้งหมดของคลาสหนึ่งจะใช้เมตาเทเบิลเดียวกัน ซึ่งเก็บเมธอดต่าง ๆ ไว้ การเปลี่ยนแปลงเมตาเทเบิลจะส่งผลต่ออินสแตนซ์ทั้งหมด

local Mt = {
  __index = {
    greet = function(self)
      print("Hello from " .. (self.name or "?"))
    end
  }
}

local a = setmetatable({name="Alice"}, Mt)
local b = setmetatable({name="Bob"},   Mt)

a:greet()   -- Hello from Alice
b:greet()   -- Hello from Bob

ฟิลด์ __metatable

การกำหนด mt.__metatable เป็นค่าใด ๆ จะทำให้ getmetatable คืนค่านั้นแทนเมตาเทเบิลจริง วิธีนี้จะป้องกันไม่ให้ผู้ใช้ตรวจสอบหรือแทนที่เมตาเทเบิล ซึ่งมีประโยชน์สำหรับการห่อหุ้มและความปลอดภัย

local secret = setmetatable({}, {
  __metatable = false,    -- hide metatable
  __index = {value = 42}
})

print(secret.value)        -- 42
print(getmetatable(secret)) -- false (not the real mt)
-- setmetatable(secret, {}) -- ERROR

การตรวจสอบเมตาเทเบิล

ใช้ getmetatable(t) เพื่อตรวจสอบว่าวัตถุมีเมตาเทเบิลที่ระบุหรือไม่ เช่น สำหรับตรวจสอบชนิดข้อมูล เนื่องจาก __metatable สามารถซ่อนเมตาเทเบิลจริงได้ จึงควรใช้ rawequal หรือฟิลด์ตัวบ่งชี้เพื่อให้การตรวจสอบอินสแตนซ์เชื่อถือได้

local MyClass = {}
MyClass.__index = MyClass
MyClass.__metatable = MyClass   -- use self as marker

function MyClass.new(x)
  return setmetatable({x=x}, MyClass)
end

local obj = MyClass.new(5)
print(getmetatable(obj) == MyClass)   -- true
print(obj.x)                           -- 5

เมตาเทเบิลกับสิ่งที่ไม่ใช่ตาราง

สตริงใน Lua มีเมตาเทเบิลอยู่แล้ว ซึ่งก็คือโมดูล string นั่นเป็นเหตุผลที่คุณสามารถเรียก s:upper() กับสตริงใด ๆ ได้ ใน Lua ล้วน ๆ คุณไม่สามารถกำหนดเมตาเทเบิลให้สตริง ตัวเลข หรือค่าบูลีนได้ ทำได้เฉพาะกับตารางและ userdata เท่านั้น

local s = "hello"
local mt = getmetatable(s)

-- String metatable has __index = string library
print(mt.__index == string)   -- true

-- So all string functions are methods:
print(s:upper())              -- HELLO
print(s:rep(3, "-"))          -- hello-hello-hello

การแก้จุดบกพร่องด้วยเมตาเทเบิล

คุณสามารถเพิ่มเมธอดแปลงค่า __tostring เพื่อควบคุมวิธีที่ tostring() และ print() ใช้แสดงตารางได้ ความสามารถนี้มีประโยชน์อย่างมากสำหรับการแก้จุดบกพร่องของวัตถุที่ซับซ้อน

local Point = {}
Point.__index = Point
Point.__tostring = function(p)
  return string.format("Point(%g, %g)", p.x, p.y)
end

function Point.new(x, y)
  return setmetatable({x=x,y=y}, Point)
end

local p = Point.new(3, 4)
print(tostring(p))   -- Point(3, 4)
print(p)             -- Point(3, 4)  (print calls tostring)

ตรวจสอบด่วน

จะเกิดอะไรขึ้นเมื่อคุณเข้าถึงคีย์ที่ไม่พบในตารางซึ่งมีเมตาเทเบิลที่กำหนด __index ให้เป็นอีกตารางหนึ่ง

ทบทวน: เมตาเทเบิล

สรุป:

  • setmetatable(t, mt) / getmetatable(t)
  • __index: ตาราง → ค้นหาในต้นแบบ; ฟังก์ชัน → ส่งต่อแบบไดนามิก
  • rawget/rawset ข้ามการทำงานของเมธอดแปลงค่า
  • เมตาเทเบิลที่ใช้ร่วมกันทำให้เกิดพฤติกรรมคล้ายคลาส
  • __metatable ป้องกันการตรวจสอบเมตาเทเบิล
  • __tostring ควบคุมผลลัพธ์ของการแสดงผล
เริ่มต้นได้ฟรี

เรียนรู้ Lua ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
40
บทเรียน
159

คำถามที่พบบ่อย

บทเรียน “รู้จักเมทาเทเบิล” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “รู้จักเมทาเทเบิล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Lua Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “รู้จักเมทาเทเบิล”

กำหนดและอ่านเมทาเทเบิลด้วย setmetatable/getmetatable คุณปฏิบัติ Lua Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Lua Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Lua Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “รู้จักเมทาเทเบิล” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Lua Academy นี้ได้ไหม

ได้ บทเรียน Lua Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. รู้จักเมทาเทเบิล
  2. __index และ __newindex
  3. เมทาเมธอดทางเลขคณิต
  4. __tostring และ __len
← กลับไปที่ Lua Academy