本文介绍cocos Lua项目的禁用创建默认环境下全局变量的做法,代码取自src\cocos\framework\init.lua,先直接上代码
-- export global variable
local __g = _G
cc.exports = {}
setmetatable(cc.exports, {
__newindex = function(_, name, value)--若需要赋值全局变量比如a=2,需要写成cc.exports.a=2
rawset(__g, name, value) --通过__newindex元方法将对cc.exports的赋值转变为对_G的赋值,因此并不对cc.exports赋值
end, --因此访问时,直接访问_G即可:print(a) --打印2
__index = function(_, name)
return rawget(__g, name)--通过__index元方法将对cc.exports的访问转变为对_G的访问
end
})
-- disable create unexpected global variable
function cc.disable_global()
setmetatable(__g, {
__newindex = function(_, name, value)--全局环境一旦赋值后会调用这里设置的__newindex元方法,提示错误,并不执行赋值
error(string.format("USE \" cc.exports.%s = value \" INSTEAD OF SET GLOBAL VARIABLE", name), 0)
end
})
end
if CC_DISABLE_GLOBAL then--CC_DISABLE_GLOBAL控制是否启用防止创建未预期的全局变量,CC_DISABLE_GLOBAL在config.lua里
cc.disable_global()
end
如果读者已对元表,__index,__newindex,rawset(),rawget()等概念和方法熟悉的话,仅阅读代码和注释即可,无需阅读下文
该段代码涉及到以下知识点:
1.元表
Lua 中的每个值都可以有一个 元表。 这个 元表 就是一个普通的 Lua 表, 它用于定义原始值在特定操作下的行为。 如果你想改变一个值在特定操作下的行为,你可以在它的元表中设置对应域。 例如,当你对非数字值做加操作时, Lua 会检查该值的元表中的 "__add
" 域下的函数。 如果能找到,Lua 则调用这个函数来完成加这个操作。
元表中的键对应着不同的 事件 名; 键关联的那些值被称为 元方法。 在上面那个例子中引用的事件为 "add"
, 完成加操作的那个函数就是元方法。
你可以用 getmetatable
函数 来获取任何值的元表,使用 setmetatable
来替换一张表的元表。
元表决定了一个对象在数学运算、位运算、比较、连接、 取长度、调用、索引时的行为。 元表还可以定义一个函数,当表对象或用户数据对象在垃圾回收时调用它。应用举例:
local mt = {}
mt.__add = function(first,second)
print("calling __add")
return first.value + second.value
end
local a = {value = 5}
local b = {value = 12}
setmetatable(a, mt)
print(a+b)
2.__
index
索引 table[key]
。 当 table
不是表或是表 table
中不存在 key
这个键时,这个事件被触发。 此时,会读出 table
相应的元方法。尽管名字取成这样, 这个事件的元方法其实可以是一个函数也可以是一张表。 如果它是一个函数,则以 table
和 key
作为参数调用它。 如果它是一张表,最终的结果就是以 key
取索引这张表的结果。 (这个索引过程是走常规的流程,而不是直接索引, 所以这次索引有可能引发另一次元方法。)
Lua查找元素的规则如下:
1.在表中查找,找到则返回,找不到则继续
2.判断是否有元表,没有返回nil,有则继续
3.判断元表有无__index方法,如果该方法为nil,则返回nil;如果是一个表,则重复①②③;如果是一个函数,则返回函数的返回值(table和key会作为参数传递进去)。
应用举例:
--__index为表
local player1 = {sex = "male"}
local tName = {name = "ivey"}
local mtName = {__index = tName}
local mtLevel = {__index = {level = 99}}
setmetatable(tName, mtLevel)
setmetatable(player1, mtName)
print(player1.sex)--player1中找到
print(player1.name)--mtName中找到
print(player1.level)--mtLevel中找到
--__index为函数
local player2 = {}
setmetatable(player2,{__index = function(table,key)
print(table,key .. " is not found")
return table,key
end})
print(player2.name)
3.__
newindex
索引赋值 table[key] = value
。 和索引事件类似,它发生在 table
不是表或是表 table
中不存在 key
这个键的时候。 此时,会读出 table
相应的元方法。同索引过程那样, 这个事件的元方法即可以是函数,也可以是一张表。 如果是一个函数, 则以 table
、 key
、以及 value
为参数传入。 如果是一张表, Lua 对这张表做索引赋值操作。 (这个索引过程是走常规的流程,而不是直接索引赋值, 所以这次索引赋值有可能引发另一次元方法。)一旦有了 "newindex" 元方法, Lua 就不再做最初的赋值操作。 (如果有必要,在元方法内部可以调用 rawset
来做赋值。)应用举例:
--__newindex为表
local player1 = {sex = "male"}
local tName = {name = "ivey"}
local mtName = {__newindex = tName}
setmetatable(player1, mtName)
player1.sex = "female"
player1.name = "Alice"
print(player1.sex)--player1中找到
print(player1.name, tName.name)--在tName中找到
--__newindex为函数
local player2 = {}
setmetatable(player2,{__newindex = function(table,key,value)
print(table, key, 0)
end})
player2.name = 15--该赋值不会执行
print(player2.name)
4.rawget(table,key)
在不触发任何元方法的情况下 获取 table[key]
的值。 table
必须是一张表; key
可以是任何值。
5.rawset(table,key,value)
在不触发任何元方法的情况下 将 table[key]
设为 value
。 table
必须是一张表, index
可以是 nil 之外的任何值。 value
可以是任何 Lua 值。函数返回 table
。
--rawget
local player1 = {sex = "male"}
setmetatable(player1, {__index = {age = 20}})
print(player1.age, rawget(player1,age))--rawget可跳过从元表取值步骤
--rawset
local player2 = {}
setmetatable(player2,{__newindex = function(table,key,value)
rawset(table,key,5)
end})
player2.age = 15--该赋值不会执行
print(player2.age)
实战遇到的面试题:
1.如何禁用全局变量
2.元表做什么用的
3.__index和__newindex的区别
来源:CSDN
作者:ellis1970
链接:https://blog.csdn.net/ellis1970/article/details/104038048