1. 程式人生 > >Lua元方法__newindex

Lua元方法__newindex

上篇部落格我們講了__index用於查詢,這一節我們講_newindex , _newindex 元方法用來對錶更新,__index則用來對錶訪問 。
當你給表的一個缺少的索引賦值,直譯器就會查詢__newindex 元方法:如果存在則呼叫這個函式而不進行賦值操作

我們先來看下簡單的賦值操作

father = {
    	house=1,
    	sayHello = function()
            print("大家好,我是father.");
        end
    }
    father.__index = father
    son = {
    	car=1
    }
    son.sayHello = function()
            print("大家好,我是son.");
        end
    setmetatable(son, father) --把son的metatable設定為father

    print(son.sayHello())

輸出值為:大家好,我是son.

我們呼叫son.sayHello,雖然son可以通過元表找到這個欄位,但是它本身不存在這個欄位,但這並影響我們給他賦值,最後呼叫sayHello成功顯示

如果我們不想給son中不存在的欄位賦值的時候如何檢測呢,這時候元方法__newindex就派上用場了,示例程式碼如下:

    local father = {
    	house=1,
    	sayHello = function()
            print("大家好,我是father.");
        end
    }
    
    local temp ={
        
        __index = father,
        __newindex = function(table, key)
            print(key .. "欄位是不存在的,不允許給它賦值!");
        end
    }
    
    son = {
    	car=1
    }
    setmetatable(son, temp) --把son的metatable設定為father
    son.sayHello = function()
            print("大家好,我是son.");
        end
    
    son.sayHello()

當我們給sayHello賦值的時候呼叫了__newindex元方法,代替了賦值操作,表son中確實不存在sayHello欄位,只是因為它的元表中有而已,因此當我們給sayHello賦值時,lua判斷sayHello不存在回去呼叫元表裡的__newindex方法,當然了,這裡的__newindex賦值不一定時function,也可以時另外一個表比如

    local pt = {
        house = 2
    }
    local father = {
    	house=0,
    	sayHello = function()
            print("大家好,我是father.");
        end
    }
    
    local temp ={
        
        __index = father,
        __newindex = pt
    }
    
    son = {
    	car=1
    }
    setmetatable(son, temp) --把son的metatable設定為father
    print("賦值前:" .. pt.house);
    son.house = 10;
    print("賦值後:" .. pt.house);
    print("son.house:" .. son.house);

輸出如下:

賦值前:2
賦值後:10
son.house:0

可以看到,當我們給son.house賦值時實際上是給pt.house賦值了。son.house本身不存在,取的是father的house值

這裡再引用下菜鳥教程中的例子加深理解下

mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })

print(mytable.key1)

mytable.newkey = "新值2"
print(mytable.newkey,mymetatable.newkey)

mytable.key1 = "新值1"
print(mytable.key1,mymetatable.key1)

 

輸出:

value1
nil    新值2
新值1    nil

當我們給mytable.newkey賦值時,會直接將值賦給mymetatable 的newkey欄位,mytable本身沒有這個欄位

當我們給mytable.key1賦值時會直接覆蓋已有值而不呼叫元方法 __newindex,此時mymetatable為nil

最後給個例子加深下理解

local pt = {
        house = 2
    }
    local father = {
    	sayHello = function()
            print("大家好,我是father.");
        end
    }
    
    local temp ={
        
        __index = father,
        __newindex  = function()
            end
    }
    
    son = {
    	car=1
    }
    setmetatable(son, temp) 
    son.house = 10;
    print("son.house:" .. son.house);
至此我們很容易判斷出son因為沒有house欄位會報錯,如果把temp中__newindex欄位賦值去掉,則能正常打印出10

簡單總結下:

如果__newindex是一個函式,則在給table不存在的欄位賦值時,會呼叫這個函式。
如果__newindex是一個table,則在給table不存在的欄位賦值時,會直接給__newindex的table賦值。

over!