javascript|對象
================================================================================
Qomolangma OpenProject v0.9
類別 :Rich Web Client
關鍵詞 :JS OOP,JS Framwork, Rich Web Client,RIA,Web Component,
DOM,DTHML,CSS,JavaScript,JScript
項目發起:aimingoo (aim@263.net)
項目團隊:aimingoo, leon(pfzhou@gmail.com)
有貢獻者:JingYu(zjy@cnpack.org)
================================================================================
八、JavaScript物件導向的支援
~~~~~~~~~~~~~~~~~~
很少有人對JavaScript的物件導向特性進行系統的分析。我希望接下來的文字讓你瞭解到這
個語言最少為人知的一面。
1. JavaScript中的類型
--------
雖然JavaScript是一個基於對象的語言,但對象(Object)在JavaScript中不是第一型的。JS
是以函數(Function)為第一型的語言。這樣說,不但是因為JS中的函數具有進階語言中的函
數的各種特性,而且也因為在JS中,Object也是由函數來實現的。——關於這一點,可以在
後文中“構造與析構”部分看到更進一步的說明。
JS中是弱類型的,他的內建類型簡單而且清晰:
---------------------------------------------------------
undefined : 未定義
number : 數字
boolean : 布爾值
string : 字串
function : 函數
object : 對象
1). undefined類型
========================
在IE5及以下版本中,除了直接賦值和typeof()之外,其它任何對undefined的操作都將導致
異常。如果需要知道一個變數是否是undefined,只能採用typeof()的方法:
<script>
var v;
if (typeof(v) == 'undefined') {
// ...
}
</script>
但是在IE5.5及以上版本中,undefined是一個已實現的系統保留字。因此可以用undefined來
比較和運算。檢測一個值是否是undefined的更簡單方法可以是:
<script>
var v;
if (v === undefined) {
// ...
}
</script>
因此為了使得核心代碼能(部分地)相容IE5及早期版本,Romo核心單元中有一行代碼用來
“聲明”一個undefined值:
//---------------------------------------------------------
// code from Qomolangma, in JSEnhance.js
//---------------------------------------------------------
var undefined = void null;
這一行代碼還有一點是需要說明的,就是void語句的應用。void表明“執行其後的語句,且
忽略傳回值”。因此在void之後可以出現能被執行的任何“單個”語句。而執行的結果就是
undefined。當然,如果你願意,你也可以用下面的代碼之一“定義undefined”。
//---------------------------------------------------------
// 1. 較複雜的方法,利用一個匿名的空函數執行的返回
//---------------------------------------------------------
var undefined = function(){}();
//---------------------------------------------------------
// 2. 代碼更簡潔,但不易懂的方法
//---------------------------------------------------------
var undefined = void 0;
void也能像函數一樣使用,因此void(0)也是合法的。有些時候,一些複雜的語句可能不能
使用void的關鍵字形式,而必須要使用void的函數形式。例如:
//---------------------------------------------------------
// 必須使用void()形式的複雜運算式
//---------------------------------------------------------
void(i=1); // 或如下語句:
void(i=1, i++);
2). number類型
========================
JavaScript中總是處理浮點數,因此它沒有象Delphi中的MaxInt這樣的常量,反而是有這
樣兩個常值定義:
Number.MAX_VALUE : 返回 JScript 能表達的最大的數。約等於 1.79E+308。
Number.MIN_VALUE : 返回 JScript 最接近0的數。約等於 2.22E-308。
因為沒有整型的緣故,因此在一些關於CSS和DOM屬性的運算中,如果你期望取值為整數2,
你可能會得到字串“2.0”——或者類似於此的一些情況。這種情況下,你可能需要用
到全域對象(Gobal)的parseInt()方法。
全域對象(Gobal)中還有兩個屬性與number類型的運算有關:
NaN : 算術運算式的運算結果不是數字,則返回NaN值。
Infinity : 比MAX_VALUE更大的數。
如果一個值是NaN,那麼他可以通過全域對象(Gobal)的isNaN()方法來檢測。然而兩個NaN
值之間不是互等的。如下例:
//---------------------------------------------------------
// NaN的運算與檢測
//---------------------------------------------------------
var
v1 = 10 * 'a';
v2 = 10 * 'a';
document.writeln(isNaN(v1));
document.writeln(isNaN(v2));
document.writeln(v1 == v2);
全域對象(Gobal)的Infinity表示比最大的數 (Number.MAX_VALUE) 更大的值。在JS中,
它在數學運算時的價值與正無窮是一樣的。——在一些實用技巧中,它也可以用來做一
個數組序列的邊界檢測。
Infinity在Number對象中被定義為POSITIVE_INFINITY。此外,負無窮也在Number中被定
義:
Number.POSITIVE_INFINITY : 比最大正數(Number.MAX_VALUE)更大的值。正無窮。
Number.NEGATIVE_INFINITY : 比最小負數(-Number.MAX_VALUE)更小的值。負無窮。
與NaN不同的是,兩個Infinity(或-Infinity)之間是互等的。如下例:
//---------------------------------------------------------
// Infinity的運算與檢測
//---------------------------------------------------------
var
v1 = Number.MAX_VALUE * 2;
v2 = Number.MAX_VALUE * 3;
document.writeln(v1);
document.writeln(v2);
document.writeln(v1 == v2);
在Global中其它與number類型相關的方法有:
isFinite() : 如果值是NaN/正無窮/負無窮,返回false,否則返回true。
parseFloat() : 從字串(的首碼部分)取一個浮點數。不成功則返回NaN。
3). boolean類型
========================
(略)
4). string類型
========================
JavaScript中的String類型原本沒有什麼特殊的,但是JavaScript為了適應
“瀏覽器實現的超文本環境”,因此它具有一些奇怪的方法。例如:
link() : 把一個有HREF屬性的超連結標籤<A>放在String對象中的文本兩端。
big() : 把一對<big>標籤放在String對象中的文本兩端。
以下方法與此類同:
anchor()
blink()
bold()
fixed()
fontcolor()
fontsize()
italics()
small()
strike()
sub()
sup()
除此之外,string的主要複雜性來自於在JavaScript中無所不在的toString()
方法。這也是JavaScript為瀏覽器環境而提供的一個很重要的方法。例如我們
聲明一個對象,但是要用document.writeln()來輸出它,在IE中會顯示什麼呢?
下例說明這個問題:
//---------------------------------------------------------
// toString()的應用
//---------------------------------------------------------
var
s = new Object();
s.v1 = 'hi,';
s.v2 = 'test!';
document.writeln(s);
document.writeln(s.toString());
s.toString = function() {
return s.v1 + s.v2;
}
document.writeln(s);
在這個例子中,我們看到,當一個對象沒有重新聲明(覆蓋)自己toString()方
法的時候,那麼它作為字串型態使用時(例如被writeln),就會調用Java Script
環境預設的toString()。反過來,你也可以重新定義JavaScript理解這個對象
的方法。
很多JavaScript架構,在實現“模板”機制的時候,就利用了這個特性。例如
他們用這樣定義一個FontElement對象:
//---------------------------------------------------------
// 利用toString()實現模板機制的簡單原理
//---------------------------------------------------------
function FontElement(innerHTML) {
this.face = '宋體';
this.color = 'red';
// more...
var ctx = innerHTML;
this.toString = function() {
return '<Font FACE="' + this.face + '" COLOR="' + this.color + '">'
+ ctx
+ '</FONT>';
}
}
var obj = new FontElement('這是一個測試。');
// 留意下面這行代碼的寫法
document.writeln(obj);
5). function類型
========================
javascript函數具有很多特性,除了物件導向的部分之外(這在後面講述),它自
已的一些獨特特性應用也很廣泛。
首先javascript中的每個函數,在調用過程中可以執有一個arguments對象。這個
對象是由指令碼解釋環境建立的,你沒有別的方法來自己建立一個arguments對象。
arguments可以看成一個數組:它有length屬性,並可以通過arguments[n]的方式
來訪問每一個參數。然而它最重要的,卻是可以通過 callee 屬性來得到正在執行
的函數對象的引用。
接下的問題變得很有趣:Function對象有一個 caller 屬性,指向正在調用當前
函數的父函數對象的引用。
——我們已經看到,我們可以在JavaScript裡面,通過callee/caller來遍曆執行
期的調用棧。由於arguments事實上也是Function的一個屬性,因此我們事實上也
能遍曆執行期調用棧上的每一個函數的參數。下面的代碼是一個簡單的樣本:
//---------------------------------------------------------
// 調用棧的遍曆
//---------------------------------------------------------
function foo1(v1, v2) {
foo2(v1 * 100);
}
function foo2(v1) {
foo3(v1 * 200);
}
function foo3(v1) {
var foo = arguments.callee;
while (foo && (foo != window)) {
document.writeln('調用參數:<br>', '---------------<br>');
var args = foo.arguments, argn = args.length;
for (var i=0; i<argn; i++) {
document.writeln('args[', i, ']: ', args[i], '<br>');
}
document.writeln('<br>');
// 上一級
foo = foo.caller;
}
}
// 運行測試
foo1(1, 2);
2. JavaScript物件導向的支援
--------
在前面的例子中其實已經講到了object類型的“型別宣告”與“執行個體建立”。
在JavaScript中,我們需要通過一個函數來聲明自己的object類型:
//---------------------------------------------------------
// JavaScript中對象的型別宣告的形式代碼
// (以後的文檔中,“對象名”通常用MyObject來替代)
//---------------------------------------------------------
function 對象名(參數表) {
this.屬性 = 初始值;
this.方法 = function(方法參數表) {
// 方法實現代碼
}
}
然後,我們可以通過這樣的代碼來建立這個物件類型的一個執行個體:
//---------------------------------------------------------
// 建立執行個體的形式代碼
// (以後的文檔中,“執行個體變數名”通常用obj來替代)
//---------------------------------------------------------
var 執行個體變數名 = new 對象名(參數表);
接下來我們來看“對象”在JavaScript中的一些具體實現和奇怪特性。
1). 函數在JavaScript的物件導向機制中的五重身份
------
“對象名”——如MyObject()——這個函數充當了以下語言角色:
(1) 普通函數
(2) 型別宣告
(3) 類型的實現
(4) 類引用
(5) 對象的建構函式
一些程式員(例如Delphi程式員)習慣於型別宣告與實現分開。例如在delphi
中,Interface節用於宣告類型或者變數,而implementation節用於書寫類型
的實現代碼,或者一些用於執行的函數、代碼流程。
但在JavaScript中,類型的聲明與實現是混在一起的。一個對象的類型(類)
通過函數來聲明,this.xxxx表明了該對象可具有的屬性或者方法。
這個函數的同時也是“類引用”。在JavaScript,如果你需要識別一個對象
的具體型別,你需要執有一個“類引用”。——當然,也就是這個函數的名
字。instanceof 運算子就用於識別執行個體的類型,我們來看一下它的應用:
//---------------------------------------------------------
// JavaScript中對象的類型識別
// 文法: 對象執行個體 instanceof 類引用
//---------------------------------------------------------
function MyObject() {
this.data = 'test data';
}
// 這裡MyObject()作為建構函式使用
var obj = new MyObject();
var arr = new Array();
// 這裡MyObject作為類引用使用
document.writeln(obj instanceof MyObject);
document.writeln(arr instanceof MyObject);
================
(未完待續)
================
接下來的內容:
2. JavaScript物件導向的支援
--------
2). 反射機制在JavaScript中的實現
3). this與with關鍵字的使用
4). 使用in關鍵字的運算
5). 使用instanceof關鍵字的運算
6). 其它與物件導向相關的關鍵字
3. 構造與析構
4. 執行個體和執行個體引用
5. 原型問題
6. 函數的上下文環境
7. 對象的類型檢查問題