首页 > lua中table的__index对于自身有什么作用?

lua中table的__index对于自身有什么作用?

学lua的时候一直有一个疑问就是关于table.__index

比如说以下代码:

lualocal a = {}

没有设置metatable的情况下,对于a来说,a.__index有什么用?


还有个问题如下:

lualocal a = {}
setmetatable(a, a)
a.__index = a

这个代码除了能够让a访问不存在的字段时报错,还有其他什么作用么?


首先在 lua 里,metatable 是一个很神奇的东西,感觉题主把 metatable 和 __index 混淆了。
在 lua 里,每一种数据类型都可以拥有 metatable,不光是 table 这种类型。只不过 table 的 metatable 你可以通过 lua 暴露出来的脚本接口修改,也就是 setmetable 来设置,而其他的 lua 数据类型的 metatable 你无法通过脚本修改,要修改只能修改 c 源码然后重新编译,同时,在 lua 里,除了 table 和 userdata 外,其他的所有类型数据都是一种类型共享同一个 metatable 的,比如所有字符串的 metatable 都是同一个。
然后再来说 metatable 有什么用?参考 lua 5.1 的实现,metatable 跟一般的 lua table 没什么区别,也是 k-v 对。你可以通过修改 metatable 的部分特殊的 key 对应的 value 内容,来达到修改这种数据一些默认操作的目的,如果你写过 C++ 应该就能明白,它有点像 C++ 里的操作符重载,比如把 + 号重载成两个数相乘这种做法。当然,lua 里的 metatable 都涉及到哪些方面呢?也就是你可以修改什么东东呢?
算术运算、比较操作、级联操作、取长度操作、索引操作,除此之外还可以设置当 userdata 被垃圾回收器回收时调用一个指定函数这种操作。
每当对 lua 中的一个数据进行上面这些操作时,就会触发一个对应的事件,然后 lua 虚拟机就会查找这个数据有没有 metatable,以及 metatable 里有没有事件对应的 k-v 存在,如果有就调用,没有就按默认的。
而在 metatable 里,事件是怎么和 k-v 对应起来的呢?通过 k 这个字符串来对应的。所有有效的 k 都是两个下划线开头的字符串,有如下这些:
__add、__sub、__mul、__div
__mod、__pow
__unm
__concat
__len
__eq、__lt、__le
__index、__newindex
__call
至于他们各自代表什么意思,可以直接去看 lua 官网的文档:链接描述

然后再来回答题主的疑问:
1、在没有设置 metatable 的情况下,对于 a 来说,a.__index 有什么用?
a.__index 只能理解成 a['__index'],理由如上,__index 即使有用,也不是 a 的 k,而应该是 a 的 metatable 的 k。
2、首先解释一下为什么题主这么写会报错,而不是我们通常见到的打印一个 table 不存在的 key 值的时候直接给 nil。
当你访问 table 的元素时,也就是当你这么写 : a1 时,其实触发了 index 事件,然后我们在 lua 的文档里可以看到触发此事件时是如何处理的:

function gettable_event (table, key)
   local h
   if type(table) == "table" then
     local v = rawget(table, key)
     if v ~= nil then return v end
     h = metatable(table).__index
     if h == nil then return nil end
   else
     h = metatable(table).__index
     if h == nil then
       error(···)
     end
   end
   if type(h) == "function" then
     return (h(table, key))     -- call the handler
   else return h[key]           -- or repeat operation on it
   end
 end

根据上面的代码,我们可以看到,题主把 a 的 metatable 设置成 a 了,然后还设置了 a 的 __index,那么有人要问了,这个 __index 到底是设置的 a 的 metatable 的还是 a 本身的呢?事实上都设置了,因为 a 的 metatable 就是它自己。所以才会出现报错的情况。按照逻辑看上面的代码实现,注意 h = metatable(table).__index 这一行,按照现在 a 的情况,那么这里 h 的值就应该是 a 自身,然后跳到函数最后发现它 return 的还是 h[key],意思是重复这个索引操作,看那行的注释也知道,这里 h 是 a,那么相当于又弄一次 a1 的流程,结果就死循环了,这是题主所谓的访问不存在字段时报错的含义。至于题主说这么弄有什么作用。在我看来,你在纯瞎搞。:)

【热门文章】
【热门文章】