11月
12
2009
分類:
最近更新:
2009-11-12
JavaScript的類別定義擴充能力
我在試探不同程式語言的中介編程與反射能力系列文章的第三篇PHP的中介編程與反射能力示範中提到 JavaScript 也可以透過 prototype 的操作實現直接擴充類別定義的能力,這種能力在 Ruby 中稱為 open class。本文是為了示範 JavaScript 此能力所做的補充。
本文的範例源碼係延續自JavaScript的中介編程與反射能力示範,除了加上 JavaScript 操作 prototype 擴充類別定義的程式碼外,順便也修改了原本的程式結構,令它可以直接透過建構子所接受的 Hash Table 內容,決定實體的可用資料欄位。不像前一版本顯著列出欄位清單。
/**
* define a constructor named 'Class'
*/
Class = new Function;
/**
* foreach
* @param f Your iterator: F(key value)
*/
Class.prototype.foreach = function(f) {
for (var p in this.props) {
f(p, this.props[p]);
}
}
/**
* accessor
* @param props 要使用預設存取器的屬性列表(hash table).
*/
Class.prototype.accessor = function(props) {
var p, sourceText;
for (p in props) {
sourceText = " \
if (v == undefined) \
return this.props.{XXX}; \
else \
this.props.{XXX} = v; \
";
this[p] = new Function("v", sourceText.replace(/{XXX}/g, p));
// Script解譯器會自動將上面的源碼解釋為:
// this[p] = function(v) {
// if (v == undefined)
// return this.props.?
// else
// this.props.? = v;
// }
}
}
/**
* 此資料類別直接透過建構子所接受的 Hash Table 內容,決定
* 實體的可用資料欄位。不像前一版本顯著列出欄位清單。
* @param args hash table
*/
function Data(args) {
if (args == undefined)
return;
this.props = {}
for (var p in args) {
this.props[p] = args[p];
}
this.accessor(this.props);
}
Data.prototype = new Class;
//定義 Data 的原型是 Class 。亦即定義 Data 為 Class 的衍生類
var d1 = new Data({
id: 1,
title: 'rock',
content: 'hello world',
timestamp: (new Date()).toLocaleString()
});
print("Properties of d1");
d1.foreach( function(name, value){
print( name + " : " + value );
});
//別種資料結果
var d2 = new Data({
'id': 1,
'title': 'rock',
'create_timestamp': (new Date()).toLocaleString(),
'update_timestamp': (new Date()).toLocaleString(),
'table': 'Data',
'gid': 100
});
print("\n---- Accessor demo ----");
d2.title('javascript'); // setter
print( d2.title() ); //getter
print("\nProperties of d2");
d2.foreach( function(name, value) {
print( name + " : " + value );
});
print( "---- More ----" );
try {
d1.sayHello();
}
catch(e) {
print( " * Sorry, they can not speak.\n" );
}
// 直接操作 Data.prototype ,添加新的定義。
Data.prototype.sayHello = function() {
print( "Hello, I am " + this.props.title );
}
// 現在它們能說 hello 了
print( " * Now, they can say hello." );
d1.sayHello();
d2.sayHello();
d2.jump = function() {
print( "I am " + this.title() + ", and I can jump.");
}
try {
d1.jump();
}
catch(e) {
print(" * Sorry, " + d1.title() + " can not jump.");
}
d2.jump();
我一開始想讓 d1, d2 向各位說聲 hello ,可惜我忘了加上去,所以系統補抓錯誤後告訴我它們不會說話。Ok, 那我就直接擴充它們的原型 (即 Data.prototype) ,加上說 hello 的能力。現在它們兩個都會說 hello 了。
我再替 d2 加上跳躍的能力,但不替 d1 加上這能力。最後再叫它們兩個跳一次,一如我所預期,只有 d2 跳起來。
這個版本和 PHP的中介編程與反射能力示範 的最後實作範例一樣,都不再明列類別可用的欄位內容,改為允許動態數量的欄位。每個資料個體都可以擁有不同的資料欄位,但我們不必定義更多的類別。
如果我們想為 Data 類別增加更多的行為能力,我們既不需要用到繼承,也不需要回到原始定義處修改。我們需要做的就只是打開 Data.prototype ,直接加上我們想要的行為。毫不拖泥帶水。
相關文章
樂多舊網址: http://blog.roodo.com/rocksaying/archives/10683081.html