学lua的时候一直有一个疑问就是关于table.__index比如说以下代码:lualocala={}在没有设置metatable的情况下,对于a来说,a.__index有什么用?还有个问题如下:lualocala={}setmetatable(a,a)a.__index=a这个代码除了能够让a访问不存在的字段时报错,还有其他什么作用么?
2 回答
一只萌萌小番薯
TA贡献1795条经验 获得超7个赞
首先在lua里,metatable是一个很神奇的东西,感觉题主把metatable和__index混淆了。在lua里,每一种数据类型都可以拥有metatable,不光是table这种类型。只不过table的metatable你可以通过lua暴露出来的脚本接口修改,也就是setmetable来设置,而其他的lua数据类型的metatable你无法通过脚本修改,要修改只能修改c源码然后重新编译,同时,在lua里,除了table和userdata外,其他的所有类型数据都是一种类型共享同一个metatable的,比如所有字符串的metatable都是同一个。然后再来说metatable有什么用?参考lua5.1的实现,metatable跟一般的luatable没什么区别,也是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的文档里可以看到触发此事件时是如何处理的:functiongettable_event(table,key)localhiftype(table)=="table"thenlocalv=rawget(table,key)ifv~=nilthenreturnvendh=metatable(table).__indexifh==nilthenreturnnilendelseh=metatable(table).__indexifh==nilthenerror(···)endendiftype(h)=="function"thenreturn(h(table,key))--callthehandlerelsereturnh[key]--orrepeatoperationonitendend根据上面的代码,我们可以看到,题主把a的metatable设置成a了,然后还设置了a的__index,那么有人要问了,这个__index到底是设置的a的metatable的还是a本身的呢?事实上都设置了,因为a的metatable就是它自己。所以才会出现报错的情况。按照逻辑看上面的代码实现,注意h=metatable(table).__index这一行,按照现在a的情况,那么这里h的值就应该是a自身,然后跳到函数最后发现它return的还是h[key],意思是重复这个索引操作,看那行的注释也知道,这里h是a,那么相当于又弄一次a1的流程,结果就死循环了,这是题主所谓的访问不存在字段时报错的含义。至于题主说这么弄有什么作用。在我看来,你在纯瞎搞。:)
添加回答
举报
0/150
提交
取消