听说是腾讯最难lua笔试题

function class(...)
    -- 返回的表类似“类”的定义
    local cls = {}
    -- 数据都存储在这里面,用作数据存储空间
    local data = {}

    -- 复制传入...的的成员。传入的...类似类定义中的成员列表。需要将父类,以及...内的成员都复制过来
    local function copyField(src, dest)
        -- 先复制父类
        if src['__super'] then
            local superMeta = getmetatable(src['__super'])
            if superMeta and superMeta['__data'] then
                for k, v in pairs(superMeta['__data']) do
                    dest[k] = v
                end
            end
        end
        -- 再复制子类,如果有重名,子类会覆盖父类
        for k, v in pairs(src) do
            if k ~= '__super' then
                dest[k] = v
            end
        end
    end
    copyField(..., data)

    -- lua 5.1之后的版本没有setfenv,引入这段代码解决setfenv问题
    if not setfenv then -- Lua 5.2
        -- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html
        -- this assumes f is a function
        local function findenv(f)
            local level = 1
            repeat
                local name, value = debug.getupvalue(f, level)
                if name == '_ENV' then
                    return level, value
                end
                level = level + 1
            until name == nil
            return nil
        end
        getfenv = function(f)
            return (select(2, findenv(f)) or _G)
        end
        setfenv = function(f, t)
            local level = findenv(f)
            if level then
                debug.setupvalue(f, level, t)
            end
            return f
        end
    end

    -- 设置cls的元表
    setmetatable(
        cls,
        {
            -- 数据存储空间
            __data = data,
            -- __newindex处理赋值时的相关逻辑,类似set
            __newindex = function(t, key, newValue)
                local oldValue = data[key]
                -- 根据旧值的类型,判断新值的类型是否相同,不相同打印提示,相同才赋值
                if oldValue then
                    if type(oldValue) == 'function' then
                        print('函数不能赋值')
                        return
                    else
                        if type(newValue) ~= type(data[key]) then
                            print('类型不匹配:', tostring(key), ' 的类型是', type(data[key]))
                            return
                        end
                    end
                end

                -- 将新值赋值
                data[key] = newValue
            end,
            -- __index处理获取值时的相关逻辑,类似get
            __index = function(t, key)
                if data[key] then
                    local value = data[key]
                    --[["
                    下面代码主要处理的是:A和B的foo函数体中都没有使用self,此时就得从全局表中获取该变量的值
                        foo = function()
                            print('from A', name, age)
                        end
                    "]]
                    if type(value) == 'function' then
                        -- 新建一个全局表
                        local newG = {}
                        setmetatable(newG, {__index = _G})
                        -- 将数据赋值到全局表中
                        for k, v in pairs(data) do
                            if type(v) ~= 'function' then
                                newG[k] = v
                            end
                        end
                        -- 设置函数的全局环境表
                        setfenv(value, newG)
                    end

                    -- 返回原始值
                    return value
                end
                return nil
            end,
            -- __call可以使class创建的对象被调用,类似构造函数的用法,调用后复制出来一个实例
            __call = function()
                local instance = {}
                setmetatable(instance, getmetatable(cls))
                return instance
            end
        }
    )

    return cls
end

--以下题干要求,以上是答案编码
--实现 class 方法
A =
    class {
    name = '',
    age = 0,
    foo = function()
        print('from A', name, age)
    end
}

B =
    class {
    __super = A,
    foo = function()
        print('from B', name, age)
    end
}

local a = A()
a.name = 'hanmeimei'
a.age = 17
a:foo()

local b = B()
b.name = 'lilei'
b.age = 18
b:foo()

a.name = 20
a.age = '20'
b.foo = 'x'

-- 要求输出

-- from A hanmeimei 17
-- from B lilei 18
-- 类型不匹配:name 的类型是 string
-- 类型不匹配:age 的类型是 number
-- 函数不能赋值

其中需要注意的知识点:Lua设置函数环境—setfenv

setfenv(f, table):设置一个函数的环境

(1)当第一个参数为一个函数时,表示设置该函数的环境 (2)当第一个参数为一个数字时,为1代表当前函数,2代表调用自己的函数,3代表调用自己的函数的函数,以此类推

*性质:函数的环境,其实一个环境就是一个表,该函数被限定为只能访问该表中的域,或在函数体内自己定义的变量。

-- 任何赋值操作都对新表进行,不用担心误操作修改了全局变量表。另外,你仍然可以通过_G修改全局变量:
newfenv = {}
setmetatable(newfenv, {__index = _G})
setfenv(1, newfenv)
print(1)        -- 1 新环境直接继承了全局环境的所有域,好处:可以不需要通过_G来手动调用

猜你喜欢

转载自blog.csdn.net/Momo_Da/article/details/104461331