JavaScript物件導向的支援(2)

來源:互聯網
上載者: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)
================================================================================

 2). 反射機制在JavaScript中的實現
 ------
  JavaScript中通過for..in文法來實現了反射機制。但是JavaScript中並不
明確區分“屬性”與“方法”,以及“事件”。因此,對屬性的類型考查在JS
中是個問題。下面的代碼簡單樣本for..in的使用與屬性識別:
//---------------------------------------------------------
// JavaScript中for..in的使用和屬性識別
//---------------------------------------------------------
var _r_event = _r_event = /^[Oo]n.*/;
var colorSetting = {
  method: 'red',
  event: 'blue',
  property: ''
}

var obj2 = {
  a_method : function() {},
  a_property: 1,
  onclick: undefined
}

function propertyKind(obj, p) {
  return  (_r_event.test(p) && (obj[p]==undefined || typeof(obj[p])=='function')) ? 'event'
    : (typeof(obj[p])=='function') ? 'method'
    : 'property';
}

var objectArr = ['window', 'obj2'];

for (var i=0; i<objectArr.length; i++) {
  document.writeln('<p>for ', objectArr[i], '<hr>');

  var obj = eval(objectArr[i]);
  for (var p in obj) {
    var kind = propertyKind(obj, p);
    document.writeln('obj.', p, ' is a ', kind.fontcolor(colorSetting[kind]), ': ', obj[p], '<br>');
  }

  document.writeln('</p>');
}

一個常常被開發人員忽略的事實是:JavaScript本身是沒有事件(Event)系統的。通常我們在JavaScript用到的onclick等事件,其實是IE的DOM模型提供的。從更核心的角度上講:IE通過COM的介面屬性公布了一組事件介面給DOM。

有兩個原因,使得在JS中不能很好的識別“一個屬性是不是事件”:

  - COM介面中本身只有方法,屬性與事件,都是通過一組get/set方法來公布的。
  - JavaScript中,本身並沒有獨立的“事件”機制。

因此我們看到event的識別方法,是檢測屬性名稱是否是以'on'字串開頭(以'On'開頭的是Qomo的約定)。接下來,由於DOM對象中的事件是可以不指定處理函數的,這種情況下事件控制代碼為null值(Qomo採用相同的約定);在另外的一些情況下,使用者可能象obj2這樣,定義一個值為 undefined的事件。因此“事件”的判定條件被處理成一個複雜的運算式:
   ("屬性以on/On開頭" && ("值為null/undefined" || "類型為function"))

另外,從上面的這段代碼的運行結果來看。對DOM對象使用for..in,是不能列舉出對象方法來的。

最後說明一點。事實上,在很多語言的實現中,“事件”都不是“物件導向”的語言特性,而是由具體的編程模型來提供的。例如Delphi中的事件驅動機制,是由Win32作業系統中的視窗訊息機制來提供,或者由使用者代碼在Component/Class中主動呼叫事件處理函數來實現。

“事件”是一個“如何驅動編程模型”的機制/問題,而不是語言本身的問題。然而以PME(property/method/event)為架構的OOP概念,已經深入人心,所以當程式設計語言或系統資料表現出這些特性來的時候,就已經沒人關心“event究竟是誰實現”的了。


 3). this與with關鍵字的使用
 ------
 在JavaScript的對象系統中,this關鍵字用在兩種地方:

   - 在構造器函數中,指代新建立的對象執行個體
   - 在對象的方法被調用時,指代調用該方法的對象執行個體

 如果一個函數被作為普通函數(而不是對象方法)調用,那麼在函數中的this關鍵字將指向window對象。與此相同的,如果this關鍵字不在任何函數中,那麼他也指向window對象。

 由於在JavaScript中不明確區分函數與方法。因此有些代碼看起來很奇怪:
//---------------------------------------------------------
// 函數的幾種可能調用形式
//---------------------------------------------------------
function foo() {
  // 下面的this指代調用該方法的對象執行個體
  if (this===window) {
    document.write('call a function.', '<BR>');
  }
  else {
    document.write('call a method, by object: ', this.name, '<BR>');
  }
}

function MyObject(name) {
  // 下面的this指代new關鍵字新建立執行個體
  this.name = name;
  this.foo = foo;
}

var obj1 = new MyObject('obj1');
var obj2 = new MyObject('obj2');

// 測試1: 作為函數調用
foo();

// 測試2: 作為對象方法的調用
obj1.foo();
obj2.foo();

// 測試3: 將函數作為“指定對象的”方法調用
foo.call(obj1);
foo.apply(obj2);

在上面的代碼裡,obj1/obj2對foo()的調用是很普通的調用方法。——也就是在構造器上,將一個函數指定為對象的方法。

而測試3中的call()與apply()就比較特殊。

在這個測試中,foo()仍然作為普通函數來調用,只是JavaScript的語言特性允許在call()/apply()時,傳入一個對象執行個體來指定foo()的上下文環境中所出現的this關鍵字的引用。——需要注意的是,此時的foo()仍舊是一個普通函數調用,而不是對象方法調用。

與this“指示調用該方法的對象執行個體”有些類同的,with()文法也用於限定“在一段程式碼片段中預設使用對象執行個體”。——如果不使用with()文法,那麼這段代碼將受到更外層with()語句的影響;如果沒有更外層的with(),那麼這段代碼的“預設使用的對象執行個體”將是window。

然而需要注意的是this與with關鍵字不是互為影響的。如下面的代碼:
//---------------------------------------------------------
// 測試: this與with關鍵字不是互為影響的
//---------------------------------------------------------
function test() {
  with (obj2) {
    this.value = 8;
  }
}
var obj2 = new Object();
obj2.value = 10;

test();
document.writeln('obj2.value: ', obj2.value, '<br>');
document.writeln('window.value: ', window.value, '<br>');

你不能指望這樣的代碼在調用結束後,會使obj2.value屬性置值為8。這幾行代碼的結果是:window對象多了一個value屬性,並且值為8。

with(obj){...}這個文法,只能限定對obj的既有屬性的讀取,而不能主動的聲明它。一旦with()裡的對象沒有指定的屬性,或者with()限定了一個不是對象的資料,那麼結果會產生一個異常。


 4). 使用in關鍵字的運算
 ------
 除了用for..in來反射對象的成員資訊之外,JavaScript中也允許直接用in關鍵字去檢測對象是否有指定名字的屬性。

 in關鍵字經常被提及的原因並不是它檢測屬性是否存在的能力,因此在早期的代碼中,很多可喜歡用“if (!obj.propName) {}” 這樣的方式來檢測propName是否是有效屬性。——很多時候,檢測有效性比檢測“是否存有該屬性”更有實用性。因此這種情況下,in只是一個可選的、官方的方案。

 in關鍵字的重要應用是高速字串檢索。尤其是在只需要判定“字串是否存在”的情況下。例如10萬個字串,如果儲存在數組中,那麼檢索效率將會極差。
//---------------------------------------------------------
// 使用對象來檢索
//---------------------------------------------------------
function arrayToObject(arr) {
  for (var obj=new Object(), i=0, imax=arr.length; i<imax; i++) {
    obj[arr[i]]=null;
  }
  return obj;
}

var
  arr = ['abc', 'def', 'ghi']; // more and more...
  obj = arrayToObject(arr);

function valueInArray(v) {
  for (var i=0, imax=arr.length; i<imax; i++) {
    if (arr[i]==v) return true;
  }

  return false;
}

function valueInObject(v) {
  return v in obj;
}

  這種使用關鍵字in的方法,也存在一些限制。例如只能尋找字串,而數組元素可以是任意值。另外,arrayToObject()也存在一些開銷,這使得它不適合於頻繁變動的尋找集。最後,(我想你可能已經注意到了)使用對象來尋找的時候並不能準確定位到尋找資料,而數組中可以指向結果的下標。



相關文章

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 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。