简体   繁体   中英

Specifying metatables on new objects in Lua

Method/1

Dog = {}
function Dog:new()
  local newObj = {sound = 'woof'}
  return setmetatable(newObj,   { __index = self })
end

Method/2

Dog = {}
function Dog:new()
  local newObj = {sound = 'woof'}
  self.__index = self
  return setmetatable(newObj, self)
end

Most of the times I have seen people using the self.__index = self method, which to me seems clumsy. Why pass the whole Dog object with all the additional methods which doesn't constitute a metatable to setmetatable ? Method/1 is good for setting the new objects's metatable.__index to the Dog object, it is also cleaner.

Is there a good reason to use Method/2 instead of Method/1 ?


Some additional code to provide context, it works with both methods

function Dog:makeSound()
  print('I say ' .. self.sound)
end

mrDog = Dog:new()
mrDog:makeSound()

Method/2 is a little more optimized than Method/1 because it doesn't need to create an extra table as its metatable. It uses itself as the metatable.

Since you said that you think Method/1 is cleaner in your question, feel free to use it. I don't think the performance difference between the two would matter in most cases. Readability is almost always more important.

While both approaches achieve the same end behavior, someone might prefer method 2 because it better conforms with the "recycle resource over creation" policy. Method 2 will always use one table Dog as the metatable, regardless of how many Dog Objects you create. Method 1, OTOH, will create a new anonymous table just to act as a meta for every Dog object created.

However, method 1 is probably easier to read and reason about for newcomers to the language since it doesn't mix the concern of metatables and object definition together.

If you want an __eq metamethod, you must have just one metatable shared between all instances or it will not work. Your method #1 will not work in this case.

But the metatable does not need to be Dog , it can be a dedicated metatable:

Method 3.

Dog = {}
local DogMeta = {__index = Dog}
function Dog:new(name)
  local newObj = {sound = 'woof', name = name}
  return setmetatable(newObj, DogMeta)
end
function DogMeta.__eq(dog1, dog2)
  return dog1.name == dog2.name
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM