JavaScript和Lua的類繼承

javascript 自己雖是一門面向對象的編程語言, 但並無明確提供繼承方式.二十多年間,衆多高手提供不少模擬繼承的實現,javascript

主要的有:對象冒充,call/apply,prototype,以及深複製等. 網上有不少此類教程,在這裏就再也不贅述這些實現.我所在的團隊正在作的項目,須要使用js和lua實現同一份API接口,已達到js和lua的無縫切換.因此,實現類的繼承方案相當重要. 接下來,就是具體實現過程, 若有不到之處,還望你們指正.java

Lua ,是一門很優秀的動態語言,爲嵌入而生,若是隻是當成腳本使用的話,類的概念其實並沒必要要, 可是若是想要構成龐大規模的代碼量,這就一個亟待的問題,由於Lua 的操做符重載的支持, 使得這一過程得以實現.編程

javascript:app

function Class(Super, init ){  //do  }

lua :   編程語言

function Class(Super , init ) --[[do]] end

*Super , 是新生成的類須要繼承的父類.函數

*init , 是新生成的類的構造函數. this

下面, 咱們就來實現這個兩個Class函數.lua

  1.在js當中,調用new算符實際就是複製當前構造函數原型.由於Lua中並無new算符,因此應該將其屏蔽.spa

  2.在lua中想要實現方法的關聯,主要使用兩種方案, 一是複製,二是覆蓋元表__index索引,複製是一個很不錯的想法,或者說是一個極其通用的思想, 繼承的本質就是讓一個實例能夠調用另一個類實例的方法.若是是這樣的的話,複製是一個很完美的方案,簡單粗暴,簡單就是穩定,粗暴就是直接;穩定直接的方案每每是實現邏輯的最佳選擇,可是想要這個過程高效,那麼就須要深厚的功力,我自認爲尚未達到這樣的水平, 因此,lua的實現機制仍是選擇覆蓋元表__index索引實現.prototype

  3. lua元表的__index索引,仔細想來,它的機制很像js的原型鏈, 也就是說讓lua模擬js的原型鏈仍是比較容易的.而原型鏈的方式實現javascript的繼承也很是容易.

基於上述3點,下面貼出代碼:

lua

local setmetatable, pairs = setmetatable, pairs;

function Class(Super, init)
    local NEW = { fz = {} };---新的類定義, fz 就是實例方法所在的域 ,至關於js的prototype域.
    NEW.__initialize = function(self, ...) ---構造方法.
        local this = {}  
        for i, v in pairs(self) do    ---- 複製的 self 副本
            this[i] = v
        end 
        
        if init then
            init(this, ...);  ---執行初始化方法
        end
        
        return setmetatable(this, {     ---創建關聯.. 若是在NEW的實例域上沒有搜索到存在的域,那麼
            __index = function(_, key)  ------就在NEW的fz下尋找. 
                return NEW.fz[key]
            end
        });
    end
    
    setmetatable(NEW.fz, {            ---創建關聯 .. 若是在NEW.fz上沒有找到存在的域,那麼
        __index = function(_, key)    ------就在Super.fz 域上尋找, 若是找不到,就返回nil.
            return Super.fz and Super.fz[key] or nil;
        end
    });
    return setmetatable(NEW, {        ---設置元表的__call 域, 使得 NEW 這個hash表可以被調用.
        __call = function(...)
            return (...).__initialize(...); --- 調用的時候直接轉到 初始化方法..
        end
    });
end

調用 : 

local M = Class({}, function(self , a , b ) ---  定義了類M , 繼承了table
    self.a = a
    self.b = b
end)
M.fz.geta = function(self)                  ---   定義實例方法 geta .
    return self.a;
end
local MM = Class(M , function(self, a , b)  ---  定義了類MM, 繼承了 M
    self.a = a
    self.b = b
end);
MM.fz.getb = function(self)                  --- 定義實例方法 getb.
    return self.b
end
local mm = MM( "AAA" , "BBB");              --- 得到 MM 的實例 mm 
print(mm:geta())                            --- "AAA"
print(mm:getb())                            --- "BBB"


上述 代碼實現仍是比較簡單的. 子類能夠繼承父類  fz 下面的全部字段...

接下來就是js 的實現了..

由於 Lua 沒有關鍵字'new' , 因此js 的實現我將new 關鍵字作了屏蔽, 此處的參考了jQuery的實現,在此對john表示敬意.. 下面就是具體代碼:

function Class(Super, init) {
    var N = function () {           //建立一個空的函數N 作一箇中間層.
    };                              
    N.prototype = Super.prototype;  // 將N的原型 指向 Super 的原型
    var New = function () {         //  要生成的類定義 NEW 
        return new New.fz.initialize(arguments);  
    };
    New.fz = New.prototype = new N();  //將 N的實例 關聯到 NEW 的原型上,並取一個別名 fz. 
    New.fz.initialize = function (M) {  //初始化方法.
        if (init) init.apply(this, M);
        return this;
    };
    New.fz.constructor = New;        // 將New.fz上的constructor域重定向到 NEW..
    New.fz.initialize.prototype = New.fz;  // 很高妙的處理,jQuery的實現.
    return New;
}


說明一下, 爲何會有一個 function  N :  實際上是一個隔離考量. 前文提到的, 繼承 只是 prototype 關聯, 對其餘域應該予以屏蔽.

因此給定一個  N , 這個 N 沒有任何實現,也沒掛載任何域, 只是將Super.prototype掛載到N.prototype上, 因此,new N() , 其實只是至關於一個指向Super.prototype的指針而已,

內存上幾乎沒有佔用.. 至於隱藏 new 關鍵字,並無選擇工廠方法的實現, 而是直接使用jQuery 的實現方案..

調用:

var M = Class({}, function (a, b) {
    this.a = a;
    this.b = b;
});
M.fz.geta = function () {
    return this.a;
};
var MM = Class(M, function (a, b) {
    this.a = a;
    this.b = b;
});
MM.fz.getb = function () {
    return this.b
};
var mm = MM("AAA", "BBB");
print(mm.geta())
print(mm.getb())

依然是跟 lua 版本 同樣的調用方式......

相關文章
相關標籤/搜索