JavaScript物件導向的支援(1)

來源:互聯網
上載者:User
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. 對象的類型檢查問題



相關文章

Cloud Intelligence Leading the Digital Future

Alibaba Cloud ACtivate Online Conference, Nov. 20th & 21st, 2019 (UTC+08)

Register Now >

Starter Package

SSD Cloud server and data transfer for only $2.50 a month

Get Started >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。