我在Lua中发现了一个有趣的问题。 Lua提供了string.format函数来格式化字符串,该函数的第一个参数是你要格式化的字符串。
无独有偶,Lua允许使用
:
操作符让对象调用第一个参数为自身的函数,这意味着下面的语句可以正常运行:
local myWord = "Hello, %s"
-- two ways to format:
string.format(myWord, "World")
myWord:format("World")
这两种风格有什么区别吗?如果没有,哪一个更适合我在我的程序中使用?
正如 Lua 中常见的情况一样,技术上存在细微的差异。但这种差异很可能对你来说根本不重要。我通常更喜欢 OOP 风格来调用字符串方法,因为它更简洁,并且不需要
string
位于范围内。
如果
myWord
保证是字符串并且字符串元表未被篡改,则这些是等价的,后者只是前者的语法糖。
至于具体细节:
string.format(myWord, "World")
在 string
(通常是 _ENV
,全局表)中查找 _G
,然后访问其中的 format
字段,然后使用 myWord
和 "World"
作为参数来调用它。
myWord:format("World")
indexes myWord
,然后访问 format
字段并使用参数 myWord, "World"
调用该字段。
如果
myWord
是字符串,索引 myWord
将命中元表,其中 string
表设置为 __index
,除非有人对字符串元表进行了 getmetatable"".__index = {}
或类似的篡改。
(Lua 沙箱中有时会发生的一个疏忽是忘记使字符串元表不可访问。)
也许行为上最相关的细微差别是,如果它是数字,
string.func(s, ...)
将强制s
转换为字符串,而s:func(...)
将抛出“尝试索引数字值”。
如果您正在维护遗留的 Lua API 并“重构”它以使用后一种风格,这可能是相关的,意外地破坏了依赖隐式数字到字符串强制转换的 API 用户的代码。
另一个可能相关的区别是,
s:func(...)
可以让你提供一个带有元表集的表s
,这样s:func(...)
可以做一些明智的事情:它可以更好地处理多态性。例如,您可以有一个 GraphemeString
“类”,它支持(其自己的版本)s:sub
,但对字素而不是字节进行操作。如果您使用“OOP”风格,则可以轻松地将一种风格替换为另一种风格。 (如果你使用“命令式”风格,你仍然可以猴子补丁string.format
等,但这会很混乱。)