Lua类Table类型属性修改作用于类原型而非实例的修复方法
问题原因
这个问题是Lua元表继承的引用共享特性导致的:
- 你把表类型的默认属性
param = {n=5}直接定义在类表TestClass上,所有通过TestClass:new()创建的实例初始是空表,本身没有param字段。 - 当你在
update方法里执行self.param.n的写操作时,Lua会通过__index元方法向上查找,直接拿到类表上共享的param表引用,修改操作自然会落到类表上,所有实例都会共享这份修改。
值类型(数字、字符串、布尔值)不会触发这个问题,因为对值类型字段赋值时会直接在当前实例表上创建新字段,不会修改元表上的原始值,但表、闭包等引用类型修改内部字段时,不会触发实例上的字段创建,会直接操作引用指向的共享对象。
修复方案
把所有引用类型的默认成员初始化逻辑放到构造函数内,保证每个实例生成时持有独立的引用副本,不要直接在类表上定义共享的引用类型默认值。
修复后的完整代码:
TestClass = {} function TestClass:new(o) o = o or {} setmetatable(o, self) self.__index = self -- 为每个新实例初始化独立的param表,支持传入自定义值覆盖默认值 o.param = o.param or { n = 5 } return o end function TestClass:update(n) n = n or 1 self.param.n = self.param.n + n end
效果验证
local obj1 = TestClass:new() local obj2 = TestClass:new() obj1:update(2) print(obj1.param.n) -- 输出7,仅修改obj1自身属性 print(obj2.param.n) -- 输出5,不受obj1操作影响 print(TestClass.param) -- 输出nil,类表不会被实例操作污染
如果需要支持实例化时传入自定义初始属性,上述写法也可以兼容,比如执行local obj3 = TestClass:new({param = {n = 100}})时,实例会优先使用传入的自定义param表,不会被默认值覆盖。
内容的提问来源于stack exchange,提问作者NotSoSeriouss
相关产品推荐
相关产品推荐

