最近更新: 2009-12-07

以JavaScript觀點來看 C++ template

從 C++ Template 到 Java Generic,一步一步來一文,我用 Java 的泛型語法改寫了一個 C++ 樣板類別。 我也用 PHP、JavaScript 和 Ruby 來做同樣的事,看看這些動態語言有沒有泛型處理能力。

我要用 JavaScript 改寫的 C++ 樣板類別,其源碼同從 C++ Template 到 Java Generic,一步一步來。本文不再重複,直接說明 JavaScript 的改寫過程。

JavaScript 改寫過程

刪掉 private, public ,將變數型態換成 var。 在 JavaScript 世界中,類別功能是透過 function object 實現,所以類別的建構子就是函數本身。將關鍵字 class 改成 function ,建構子參數跟著移到第一行。更多關於 JavaScript 實現類別的細節,請看 JavaScript的中介編程與反射能力示範掌握 JavaScript 的「封裝」特性, part 1

透過 undefined 或 arguments 判斷參數狀態設定實體初始狀態。 無參數建構子: N() {d = 0},意即 arguments.length == 0 時, d 之初值為0。 單參數建構子: N(int v) {d = v},意即 v 存在且為數值時, d 之初值為 v。 將上述 C/C++ 中分成兩個函數撰寫的流程彙整後即為下列流程:

if (v === undefined) {// or arguments.length == 0
    d = 0;
else
    d = v;

當我們利用函數覆載撰寫多個建構子時,我們腦中就應該浮現出上述的配對流程, 而且事實上,編譯器內部也是使用著相同的配對流程決定該調用哪個建構子。 一般而言,靜態語言將這些動作內隱在編譯動作中,而動態語言則是顯露於程式碼。

將 C/C++ 的 method 定義語法改成 JavaScript 的寫法:

//C++:
type methodName(arguments...) {body...}

//JavaScript:
this.methodName = function(arguments...) {body...}

以 JavaScript 改寫類別 Cx 不需要特殊的樣板語法。

function Cx(v) {
    var data;

    data = v;

    this.getData = function() {
        return data.value();
    }
};

function N(v) {
    var n;

    if (v === undefined)
        n = 0;
    else
        n = v;

    this.value = function() {
        return -n;
    }
};

function M(v) {
    var m;

    if (v === undefined)
        m = 0;
    else
        m = v;

    this.value = function () {
        return m * 10;
    }
};

function S(v) {
    var s;

    if (v === undefined)
        s = "";
    else
        s = v;

    this.value = function() {
        return s;
    }
};

緊接著上面的內容,接著的要改寫 C++ 的 main() 。在 JavaScript 中不需要指示程序進入點,直接寫即可。

JavaScript 的變數不用宣告型別,寫 var 即可,由右值的實體決定左值的型別。 C# 3.0 引入了類似觀念,稱為隱含型別,而且關鍵字就是 var。所以在 C# 3.0 的程式碼中看到 var n = new N(1); 時,請勿訝異。但是 JavaScript 的變數可以再次指派其他型別的資料給左值;C# 3.0 則在第一次指派後便固定型別,之後不可指派其他型別的內容給變數。

var n = new N(1);

//不用寫特殊的樣板語法...
var cn = new Cx( n );

print( cn.getData() );

var m = new M(1);
var cm = new Cx( m );
print( cm.getData() );

var s = new S("hello");
var cs = new Cx( s );
print( cs.getData() );

在 C++ 中的樣板類別 Cx ,到了 JavaScript 中之後,跟一般的類別沒兩樣。 因為動態語言的語義,基本上就是泛型的。

有些沒接觸過動態語言的人,對於泛型有一種奇怪的誤解,他們認為動態語言並沒有泛型語法,所以動態語言沒有泛型能力。我在C++和動態語言的泛型一文中曾經駁斥過這一論點。從語意看,動態語言其實就是泛型的。

移除 C++ 型別資訊,以 JsvaScript 語法改寫的 N, M, S 類別定義,也僅僅只剩兩行程式碼不同,這意味著我們可以進一步重構彙整。而且 JavaScript 做起來也不難。

相關文章
樂多舊網址: http://blog.roodo.com/rocksaying/archives/10934753.html

樂多舊回應
edwardsayer@gmail.com(Edward) (#comment-20155045)
Tue, 08 Dec 2009 13:27:02 +0800
我會說,在動態語言中,沒有泛型的觀念,一樣活的很好。因為根本不太需要去考慮介面上型別轉換的問題。比較辛苦的是,測試案例得多寫一點。