讲到元表,先看一段table的合并动作.

  1. t1 = {1,2}
  2. t2 = {3,4}
  3. t3 = t1 + t2
  1. attempt to perform arithmetic on a table value (global 't1')

程序会报错,因为不知道如何对两个table执行+运算,这个时候就需要通过元表来定义,有点类似c中的运算符加载。我们看一下如何通过元表实现合并操作。


  1. local mt = {}
  2. --定义mt.__add元方法(其实就是元表中一个特殊的索引值)为将两个表的元素合并后返回一个新表
  3. mt.__add = function(t1,t2)
  4. local temp = {}
  5. for _,v in pairs(t1) do
  6. table.insert(temp,v)
  7. end
  8. for _,v in pairs(t2) do
  9. table.insert(temp,v)
  10. end
  11. return temp
  12. end
  13. local t1 = {1,2,3}
  14. local t2 = {2}
  15. --设置t1的元表为mt
  16. setmetatable(t1,mt)
  17. local t3 = t1 + t2
  18. --输出t3
  19. local st = "{"
  20. for _,v in pairs(t3) do
  21. st = st..v..", "
  22. end
  23. st = st.."}"
  24. print(st)
  1. {1, 2, 3, 2, }

可以看到, 程序在执行的时候,调用了mt._add元方法计算。

具体的过程是:

1.查看t1是否有元表,若有,则查看t1的元表是否有__add元方法,若有则调用。

2.查看t2是否有元表,若有,则查看t2的元表是否有__add元方法,若有则调用。

3.若都没有则会报错。

所以说,我们通过定义了t1元表的__add元方法,达到了让两个表通过+号来相加的效果

Lua 查找一个表元素时的规则,其实就是如下 3 个步骤:

1.在表中查找,如果找到,返回该元素,找不到则继续

2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。

3.判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil;如果 __index 方法是一个表,则重复 1、2、3;如果 __index 方法是一个函数,则返回该函数的返回值。

Lua提供两个用来处理元表的方法

  • setmetatable(table, metatable)为表设置元表metatable,不能从Lua中改变其它任何类型的值的元表metatable(使用debug库例外),要这样做的话必须使用C语言的API。

  • getmetatable(table)获取表的元表metatable对象

元表的元方法有:(下标是__双底线喔)

函数 描述
__add 运算符 +
__sub 运算符 -
__mul 运算符 *
__ div 运算符 /
__mod 运算符 %
__unm 运算符 -(取反)
__concat 运算符 ..
__eq 运算符 ==
__lt 运算符 <
__le 运算符 <=
__call 当函数调用
__tostring 转化为字符串
__index 调用一个索引
__newindex 给一个索引赋值

__index取下标操作用于访问`table[key]

__newindex赋值给指定下标`table[key]=value

__tostring转换成字符串

__call当Lua调用一个值时调用

__mode用于弱表`week table

__metatable用于保护metatable不被访问

  • __add

当Lua试图将两个表相加时,会首先检查两个表之一是否有元素,然后检查该元表中是否具有一个叫做__add的字段。如果Lua找到了该字段,则会调用该字段对应的值。这个值就是所谓的“元方法”。


  1. local tbl1 = {1,2,3}
  2. local tbl2 = {5,1,1}
  3. -- print(#tbl1, #tbl2)
  4. -- 无法直接相加两个表
  5. -- printf(tbl1 + tbl2)
  6. -- 实现表的相加操作
  7. mt = {}
  8. mt.__add = function(x, y)
  9. local result = {}
  10. local length = #x
  11. for i=1,length do
  12. result[i] = x[i] + y[i]
  13. end
  14. return result
  15. end
  16. -- test
  17. -- 设置表的元表
  18. setmetatable(tbl1, mt)
  19. setmetatable(tbl2, mt)
  20. -- 执行表的相加操作
  21. local result = tbl1 + tbl2
  22. -- 循环输出
  23. for k,v in ipairs(result) do
  24. print(k, v)
  25. end
  1. 1 6
  2. 2 3
  3. 3 4
  • __concat 表连接
  1. -- 表的连接
  2. local tbl1 = {1,2,3}
  3. local tbl2 = {2,3,4}
  4. -- 实现表的相加操作
  5. mt = {}
  6. mt.__add = function(x, y)
  7. local result = {}
  8. local length = #x
  9. for i=1,length do
  10. result[i] = x[i] + y[i]
  11. end
  12. return result
  13. end
  14. -- 实现表的连接操作
  15. mt.__concat = function(x, y)
  16. local length = #x
  17. local result = {}
  18. for i=1,length do
  19. result[i] = x[i].."**"..y[i]
  20. end
  21. return result
  22. end
  23. -- 设置表的元表
  24. setmetatable(tbl1, mt)
  25. setmetatable(tbl2, mt)
  26. -- 执行表的连接操作
  27. local result = tbl1..tbl2
  28. -- 循环输出
  29. for k,v in ipairs(result) do
  30. print(k, v)
  31. end
  1. 1 1**2
  2. 2 2**3
  3. 3 3**4

下面举一个例子,集合运算

使用表table表示集合,实现集合计算中的并集、交集等。大家看一下写法,体会一下元表的运用


  1. -- 定义集合
  2. Set = {}
  3. -- 定义集合的元表
  4. local mt = {}
  5. -- 创建集合,根据参数列表值创建新集合
  6. function Set.new(arr)
  7. local set = {}
  8. setmetatable(set, mt) --创建集合均具有一个相同的元表
  9. for i,v in ipairs(arr) do
  10. set[v] = true
  11. end
  12. return set
  13. end
  14. -- 求集合并集
  15. function Set.union(x, y)
  16. local result = Set.new{}
  17. for k,v in pairs(x) do
  18. result[k] = true
  19. end
  20. for k,v in pairs(y) do
  21. result[k] = true
  22. end
  23. return result
  24. end
  25. -- 求集合交集
  26. function Set.intersection(x, y)
  27. -- 创建空集合
  28. local result = Set.new{}
  29. for k in pairs(x) do
  30. result[k] = y[k]
  31. end
  32. return result
  33. end
  34. -- 将集合转换为字符串
  35. function Set.tostring(set)
  36. -- 定义存放集合中所有元素的列表
  37. local result = {}
  38. for k in pairs(set) do
  39. result[#result + 1] = k
  40. end
  41. return "{"..table.concat(result, ", ").."}"
  42. end
  43. -- 打印输出集合元素
  44. function Set.print(set)
  45. print(Set.tostring(set))
  46. end
  47. -- 设置集合元方法
  48. mt.__add = Set.union
  49. -- 测试
  50. local set1 = Set.new{10,20,30,40}
  51. local set2 = Set.new{30, 1,50}
  52. Set.print(set1 + set2) -- {1, 40, 10, 20, 30, 50}
  53. Set.print(Set.intersection(set1, set2)) -- {30}
  • __call

__call可以让table当做一个函数来使用。

  1. local mt = {}
  2. --__call的第一参数是表自己
  3. mt.__call = function(mytable,...)
  4. --输出所有参数
  5. for _,v in ipairs{...} do
  6. print(v)
  7. end
  8. end
  9. t = {}
  10. setmetatable(t,mt)
  11. --将t当作一个函数调用
  12. t(1,2,3)
  1. 1
  2. 2
  3. 3

再举一个例子,注意call 里面的参数调用

  1. local mt = {}
  2. sum = 0
  3. --__call的第一参数是表自己
  4. mt.__call = function(mytable,val)
  5. --输出所有参数
  6. for i = 1,#mytable do
  7. sum = sum +mytable[i]*val
  8. end
  9. return sum
  10. end
  11. t = {1,2,3}
  12. setmetatable(t,mt)
  13. --将t当作一个函数调用
  14. print(t(5))
  15. --30
  • __tostring

__tostring可以修改table转化为字符串的行为

  1. local mt = {}
  2. --参数是表自己
  3. mt.__tostring = function(t)
  4. local s = "{"
  5. for i,v in ipairs(t) do
  6. if i > 1 then
  7. s = s..", "
  8. end
  9. s = s..v
  10. end
  11. s = s .."}"
  12. return s
  13. end
  14. t = {1,2,3}
  15. --直接输出t
  16. print(t)
  17. --将t的元表设为mt
  18. setmetatable(t,mt)
  19. --输出t
  20. print(t)
  1. table: 0x7fcfe7c06a80
  2. {1, 2, 3}
  • __index

调用table的一个不存在的索引时,会使用到元表的__index元方法,和前几个元方法不同,__index可以是一个函数也可是一个table。

作为函数:

将表和索引作为参数传入__index元方法,return一个返回值

  1. local mt = {}
  2. --第一个参数是表自己,第二个参数是调用的索引
  3. mt.__index = function(t,key)
  4. return "it is "..key
  5. end
  6. t = {1,2,3}
  7. --输出未定义的key索引,输出为nil
  8. print(t.key)
  9. setmetatable(t,mt)
  10. --设置元表后输出未定义的key索引,调用元表的__index函数,返回"it is key"输出
  11. print(t.key)
  1. local tbl = {x=1, y=2}
  2. -- table中字段默认值为nil
  3. print(tbl.x, tbl.y, tbl.z) -- 1 2 nil
  4. -- 通过metatable修改table的默认值
  5. function setTableDefault(tbl, default)
  6. local mt = {
  7. __index = function()
  8. return default
  9. end
  10. }
  11. setmetatable(tbl, mt)
  12. end
  13. -- 调用setTableDefault后,任何对tbl中存在的字段的访问都回调用它的__index
  14. setTableDefault(tbl, 1)
  15. print(tbl.x, tbl.y, tbl.z) -- 1 2 1

作为table:

查找__index元方法表,若有该索引,则返回该索引对应的值,否则返回nil

  1. local mt = {}
  2. mt.__index = {key = "it is key"}
  3. t = {1,2,3}
  4. --输出未定义的key索引,输出为nil
  5. print(t.key)
  6. setmetatable(t,mt)
  7. --输出表中未定义,但元表的__index中定义的key索引时,输出__index中的key索引值"it is key"
  8. print(t.key)
  9. --输出表中未定义,但元表的__index中也未定义的值时,输出为nil
  10. print(t.key2)
  1. nil
  2. it is key
  3. nil
  • __newindex

__newindex__index类似,不同之处在于__newindex用于table的更新,__index用于table的查询;当为table中一个不存在的索引赋值时,会去调用元表中的__newindex元方法

1.作为函数

__newindex是一个函数时会将赋值语句中的表、索引、赋的值当作参数去调用。不对表进行改变

  1. local mt = {}
  2. --第一个参数时表自己,第二个参数是索引,第三个参数是赋的值
  3. mt.__newindex = function(t,index,value)
  4. print("index is "..index)
  5. print("value is "..value)
  6. end
  7. t = {key = "it is key"}
  8. setmetatable(t,mt)
  9. --输出表中已有索引key的值
  10. print(t.key)
  11. --为表中不存在的newKey索引赋值,调用了元表的__newIndex元方法,输出了参数信息
  12. t.newKey = 10
  13. --表中的newKey索引值还是空,上面看着是一个赋值操作,其实只是调用了__newIndex元方法,并没有对t中的元素进行改动
  14. print(t.newKey)
  1. it is key
  2. index is newKey
  3. value is 10
  4. nil

  1. -- 定义原表
  2. local mt = {}
  3. mt.__index = function(tbl, key)
  4. return mt[key]
  5. end
  6. mt.__newindex = function(tbl, key, value)
  7. mt[key] = value
  8. print(string.format("modify: key=%s value=%s", key, value))
  9. end
  10. local window = {x=1}
  11. setmetatable(window, mt)
  12. print(window.x) -- 1
  13. print(rawget(window, x)) -- nil
  14. -- 添加属性
  15. print ("-------------")
  16. window.y = 2
  17. print ("-------------")
  18. for k,v in pairs(mt) do
  19. print (k,v)
  20. end
  21. print ("-------------")
  22. for k in pairs(mt) do
  23. print (k)
  24. end
  25. print ("-------------")
  26. print(window.y) -- 2
  27. print(rawget(window, y)) -- nil
  1. 1
  2. nil
  3. -------------
  4. modify: key=y value=2
  5. -------------
  6. __index function: 0x7fde254066f0
  7. y 2
  8. __newindex function: 0x7fde25406b00
  9. -------------
  10. __index
  11. y
  12. __newindex
  13. -------------
  14. 2
  15. nil

2.作为table

__newindex是一个table时,为t中不存在的索引赋值会将该索引和值赋到__newindex所指向的表中,不对原来的表进行改变。

  1. local mt = {}
  2. --将__newindex元方法设置为一个空表newTable
  3. local newTable = {}
  4. mt.__newindex = newTable
  5. t = {}
  6. setmetatable(t,mt)
  7. print(t.newKey,newTable.newKey)
  8. --对t中不存在的索引进行负值时,由于t的元表中的__newindex元方法指向了一个表,所以并没有对t中的索引进行赋值操作将,而是将__newindex所指向的newTablenewKey索引赋值为了"it is newKey"
  9. t.newKey = "it is newKey"
  10. print(t.newKey,newTable.newKey)
  1. nil nil
  2. nil it is newKey

当然如果主表中存在该索引,自然会直接赋值,不会传递元表中赋值。我们也可以直接改写newindex,用rawset直接赋值


  1. Window = {}
  2. Window.mt = {}
  3. function Window.new(o)
  4. setmetatable(o ,Window.mt)
  5. return o
  6. end
  7. Window.mt.__index = function (t ,key)
  8. return 1000
  9. end
  10. Window.mt.__newindex = function (table ,key ,value)
  11. if key == "wangbin" then
  12. rawset(table ,"wangbin" ,"yes,i am")
  13. end
  14. end
  15. w = Window.new{x = 10 ,y = 20}
  16. w.wangbin = "55"
  17. print(w.wangbin)
  1. yes,i am

rawget 和 rawset

有时候我们希望直接改动或获取表中的值时,就需要rawget和rawset方法了。

rawget可以让你直接获取到表中索引的实际值,而不通过元表的__index元方法。

rawget是为了绕过__index而出现的,直接点,就是让__index方法的重写无效

  1. local mt = {}
  2. mt.__index = {key = "it is key"}
  3. t = {}
  4. setmetatable(t,mt)
  5. print(t.key)
  6. --通过rawget直接获取t中的key索引
  7. print(rawget(t,"key"))
  1. it is key
  2. nil

rawset可以让你直接为表中索引的赋值,而不通过元表的__newindex元方法。

  1. local mt = {}
  2. local newTable = {}
  3. mt.__newindex = newTable
  4. t = {}
  5. setmetatable(t,mt)
  6. print(t.newKey,newTable.newKey)
  7. --通过rawset直接向tnewKey索引赋值
  8. rawset(t,"newKey","it is newKey")
  9. print(t.newKey,newTable.newKey)
  1. nil nil
  2. it is newKey nil
  1. local mt = {}
  2. t = {}
  3. setmetatable(t,mt)
  4. rawset(t,"newKey","it is newKey")
  5. for k ,v in pairs (t) do
  6. print (k,v)
  7. end
  8. print(t.newKey)
  1. newKey it is newKey
  2. it is newKey

下面举几个例子,讲述一下各个方法之间的关系。

  1. local tb = {}
  2. setmetatable(tb, { __index = function()
  3. return "not find"
  4. end })
  5. setmetatable(tb, { __newindex = function(table, key, value)
  6. local patchKey = "version"
  7. if key == patchKey then
  8. rawset(table, patchKey, "补丁值")
  9. else
  10. rawset(table, key, value)
  11. end
  12. end })
  13. -- setmetatable(tb, { __index = function()
  14. -- return "not find"
  15. -- end })
  16. tb.version = "正常版本"
  17. tb.date = "2018"
  18. print(tb.version) --打印 补丁值
  19. print(tb.server) --打印nil,不会调用__index方法了?
  20. print(tb.date) --打印2018

经过测试发现:

如果__index在__newindex之前,则不会调用__index

如果把_index放在__newindex之后,调用不存在值,才会调用__index方法

--谁在后面就会调用谁,前一个会失效。但是这个取决于你定于元方法的方式(我们一般定义元方法方式如下),看下面的写法没问题;


  1. local tb = {}
  2. local mt ={}
  3. mt.__newindex = function(table, key, value)
  4. local patchKey = "version"
  5. if key == patchKey then
  6. rawset(table, patchKey, "补丁值")
  7. else
  8. rawset(table, key, value)
  9. end
  10. end
  11. mt.__index = function()
  12. return "not find"
  13. end
  14. setmetatable(tb,mt)
  15. tb.version = "正常版本"
  16. tb.date = "2018"
  17. print(tb.version)
  18. print(tb.server)
  19. print(tb.date)
  1. 补丁值
  2. not find
  3. 2018

rawget是为了绕过__index而出现的,直接点,就是让__index方法的重写无效

  1. --- Gets the real value of `table[index]`, the `__index` metamethod. `table`
  2. --- must be a table; `index` may be any value.
  3. ---@param table table
  4. ---@param index any
  5. ---@return any
  6. function rawget(table, index) end
  1. local tb = {}
  2. local mt ={mm = "test"}
  3. mt.__index = function()
  4. return "not find"
  5. end
  6. setmetatable(tb,mt)
  7. tb.version = "正常版本"
  8. print(tb.version)
  9. print(tb.server) ---不存在的值,调用__index方法
  10. --rawget是为了绕过__index而出现的,直接点,就是让__index方法的重写无效
  11. print(rawget(tb, "version")) --打印 正常版本
  12. print(rawget(tb, "server")) --打印nil

利用元表的特性实现对象继承


  1. local function class( super )
  2. local cls
  3. if super then
  4. cls = {}
  5. cls.super = super
  6. setmetatable(cls, {__index = super})
  7. else
  8. -- ctor是构造函数的命名
  9. cls = {ctor = function () end}
  10. end
  11. cls.__index = cls
  12. function cls.new( ... )
  13. local instance = setmetatable({}, cls)
  14. instance:ctor()
  15. return instance
  16. end
  17. return cls
  18. end
  19. --测试实现部分
  20. local Test = class()
  21. function Test:doSomething()
  22. print("test doSomething")
  23. end
  24. local test = Test.new()
  25. test:doSomething()
  26. --测试继承部分
  27. local Test = class()
  28. function Test:doSomething()
  29. print("test doSomething")
  30. end
  31. local Test2 = class(Test)
  32. local test = Test2.new()
  33. test:doSomething()

在new的时候,创建一个table并返回,即创建一个实例,实例可以有自己的字段,比如Test类的doSomething,该字段是个函数,可以调用执行。实例的元表是cls,如果调用实例没有的字段,会去cls里找

cls设置了元方法__index = cls

如果没有super,则只有一个构造函数方法

如果有super,cls的元表是super,元表的元方法也正确的设置了

所以,在Test2是继承自Test的,它的实例test调用doSomething,找不到,去元表里找,元表发现自己有父类,去父类里找,成功找到。

多继承

如果我想要继承多个父类,怎么办?

思路就是将元方法改成函数


  1. local function search(key, tables)
  2. for _, super in ipairs(tables) do
  3. if super[key] then
  4. return super[key]
  5. end
  6. end
  7. return nil
  8. end
  9. local function class(...)
  10. local cls = { ctor = function () end}
  11. local supers = {...}
  12. setmetatable(cls, {__index = function (_, key)
  13. -- 在查找table的时候,会把tablekey传进来
  14. return search(key, supers)
  15. end})
  16. function cls.new(...)
  17. local instance = {}
  18. setmetatable(instance, {__index = cls})
  19. instance:ctor(...)
  20. return instance
  21. end
  22. return cls
  23. end
  24. local Human = class()
  25. function Human:life()
  26. print("almost 100 years.")
  27. end
  28. local Programmer = class()
  29. function Programmer:coding()
  30. print("sub 1 year.")
  31. end
  32. local My = class(Human, Programmer)
  33. local You = My.new()
  34. You:life()
  35. You:coding()
  1. almost 100 years.
  2. sub 1 year.

解析:

在You里找不到life和coding字段,去找元表cls,调用元方法__index,__index调用函数search,把所有的父类都找一遍

成功找到

Lua 学习之基础篇八<Lua 元表(Metatabble)&&继承>的更多相关文章

  1. Lua 学习之基础篇七<Lua Module,Package介绍>

    Lua 之Module介绍 包管理库提供了从 Lua 中加载模块的基础库. 只有一个导出函数直接放在全局环境中: [require]. 所有其它的部分都导出在表 package 中. require ...

  2. Lua 学习之基础篇四<Lua table(表)>

    table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组.字典等. Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil. Lua ta ...

  3. Lua 学习之基础篇十<Lua 常见的语法规则>

    下面讲一些lua 常见的用法和规则,可以为学习理解lua带来帮助,最后附上的部分是lua的基本操作,基本包含所有常用语法语句. 1. if判断 lua把 nil 和false 视为"假&qu ...

  4. Lua 学习之基础篇一<Lua 运算符>

    引言 由于刚接触lua,个人觉得接触一门新语言,就要一定要对基础的部分做一个快速了解. 于是参考网上相关资料吸收并整理下来作为笔记,模糊的时候用来回顾一下. 这些部分基本都是经过自己手动测试梳理过,没 ...

  5. Lua 学习之基础篇二<Lua 数据类型以及函数库 汇总>

    引言 前面讲了运算符,这里主要对Lua的数据处理相关的数据类型和函数库进行总结归纳,后面会再接着单独分开讲解具体使用. 首先因为Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值. 值可以存储 ...

  6. Lua 学习之基础篇九<Lua 协同程序(Coroutine)>

    引言 讲到协程,首先来介绍一下线程和协程的区别 lua协程和多线程 相同之处:拥有自己独立的桟.局部变量和PC计数器,同时又与其他协程共享全局变量和其他大部分东西 不同之处:一个多线程程序可以同时运行 ...

  7. Lua 学习之基础篇六<Lua IO 库>

    引言 I/O 库提供了两套不同风格的文件处理接口. 第一种风格使用隐式的文件句柄: 它提供设置默认输入文件及默认输出文件的操作, 所有的输入输出操作都针对这些默认文件. 第二种风格使用显式的文件句柄. ...

  8. Lua 学习之基础篇五<Lua OS 库>

    lua os库提供了简单的跟操作系统有关的功能 1.os.clock() 返回程序所运行使用的时间 local nowTime = os.clock() print("now time is ...

  9. Lua 学习之基础篇三<Lua 字符串操作>

    Lua字符串可以使用以下三种方式表示: 单引号间的一串字符. 双引号间的一串字符. [[和]]间的一串字符. string = [["Lua"]] print("字符串 ...

随机推荐

  1. mysql的AB及读写和集群

    Mysql的AB及读写  第1章 Mysql的AB配置 1.1 master配置 1.2 slave配置 第2章 读写分离 2.1 安装mycat 2.2 启动mycat 2.3 登录mycat相关问 ...

  2. Linux内核编译、安装流程

    原文链接:https://blog.csdn.net/qq_28437139/article/details/83692907 此处只讲linux内核编译步骤至于安装虚拟机,安装ubuntu操作系统请 ...

  3. [转帖]为何 CPU 只用硅,而不用能耗更低的锗制作?知乎好文章

    作者:鲁超链接:https://www.zhihu.com/question/28935966/answer/617701106来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...

  4. PS错误1

    PS错误1 提示要卸载.不用卸载直接安装即可.还保留了之前的设置. 在安装目录下看看有没有安装PS的exe程序.可能有.

  5. 用css美化select框

    先上代码: .selectData{ height: 0.42rem; position: absolute; right:.28rem; top:.30rem; //去边框 border: none ...

  6. Java的设计模式(4)--抽象工厂模式

    提供一个创建一系列或相互依赖对象的接口,而无须指定他们具体的类.例如某些系统可能需要为用户提供一系列相关对象,但系统不希望用户直接使用new运算符实例化这些对象,而是应当由系统来控制这些对象的创建,否 ...

  7. js 颜色随机切换

    生成随机颜色 方法1:RGB模式 function randomColor1() { var r=Math.floor(Math.random()*256); var g=Math.floor(Mat ...

  8. S03_CH08_DMA_LWIP以太网传输

    S03_CH08_DMA_LWIP以太网传输 8.1概述 本例程详细创建过程和本季课程第一课<S03_CH01_AXI_DMA_LOOP 环路测试>非常类似,因此如果读者不清楚如何创建工程 ...

  9. Scratch:海龟绘图(九)

    在本课的前导部分,我们说到怎么做才能成为一个负责任的“程序猿”.我认为,负责任的程序员决不会草率的处理任何“函数接口”. 比如这个“画圆”函数,程序员就会认真推敲“哪些参数是必要的.哪些参数又是多余的 ...

  10. react-router 5.0 的鉴权

    react-router 5.0 的鉴权 当我们使用react-router 控制页面的路由时候,有些页面,是需要登录才能访问,有些不需要登录就可以访问,还有些页面,是根据用户的权限来限制访问的. 如 ...