[from] http://birdshome.cnblogs.com/archive/2005/06/07/169168.html
在"JavaScript中this關鍵字使用方法詳解"一文中,我曾例舉了在JavaScript和JScript中的8種this關鍵字的方式。這不又發現還有兩種this關鍵字的使用方式當時沒有說到,現補充說明一下。并且通過第一種this關鍵字使用的說明,能讓我們更好的理解JavaScript作為Object-Based語言的本質。
??? 一種是和JavaScript類中的定義有關,我們知道當我們定義如下類的時候:?










??? 方法ToString中的this.m_Properties就是100,那么下面這種定義呢?











??? 這時ToString中的這個this.m_Properties是啥?是-100。一定嗎?這個不一定了,這得看我們怎么調用這個ToString方法。



??? 這時的運行結果居然是:-100和undefined。真是郁悶哈,怎么才能運行fun得到-100呢?需要這樣來為fun賦值:
?


??? 呵呵,這樣就是-100了。好像挺廢話的,最終不還是調用的JSClass.ToString()嗎?這個我們后面再說,看看把這兩個JSClass合在一起是什么情況呢?
















??? 這兩個ToString()方法,和里面的this關鍵字它們是什么關系呢?看下面的示例:



??? 結果為:100和-100。這里的jsclass實際上是JavaScript的語言機制,通過new關鍵字的說明來創建的一個新的實例。而JSClass是什么呢?他們本就是對象實例,只是長得像個函數,也像一個類的說明。對于this的問題,第一個jsclass.ToString()方法里的this是指新創建實例,而JSClass.ToString()方法里的this是值得JSClass這個對象實例。
??? 理解了這兩個this所指代的東西的不同本質,也就能更好的明白,為什么JavaScript叫做Object-Based的語言,而更加清楚它和Object-Oriented語言的本質區別。
??? 回頭再說一下補充第二中this的使用方法,就是在使用eval方法的時候,這個時候的this是什么呢?看看:

??? 的結果是什么?是true!這是因為eval內代碼被執行的作用域就是當前頁面本身的說。
posted on 2005-06-07 13:37 birdshome 閱讀(1883) 評論(10) ?編輯?收藏收藏至365Key 所屬分類: JScript&DHTML開發
評論
# ?re: JScript中的"this"關鍵字使用方式補充??回復??
JSClass.m_Properties = -100;這種方式是定義靜態屬性,當然不能用this來調用啦。
# ?re: JScript中的"this"關鍵字使用方式補充??回復??
有種類叫Function,任何函數/構造器,都是它的實例改變這個構造器的原型中的屬性自然和改變這個構造器對象的屬性是不一樣的
# ?re: JScript中的"this"關鍵字使用方式補充??回復??
一點心得,請birdhome兄斧正!在javascript里面,自定義類的定義中的this和該類的方法定義中的this不一樣,得用call函數或者apply函數轉換。這對于用慣了c#剛剛接觸javascript面向對象編程的朋友(本人就是其中之一)來說是個容易出錯的地方。javascript函數定義中的this就是指函數本身,不能想當然的認為它指的是外層的類實例或者其他什么別的東西。prototype.js里面的bind方法可以用來轉換這個this。而且利用javascript函數的一個似乎是比較奇怪的特性——函數定義的參數個數可以跟函數調用時傳入的參數個數完全不同,可以很方便的實現轉換過程中的多參數傳遞問題,也就是說只要寫一個bind函數,就可以轉換任意函數的上下文。
Function.prototype.Bind = function(obj){
var method = this;
return function(){
method.apply(obj,arguments);
}
}
在類定義中,把obj換成this,然后調用已定義方法的Bind方法,就可以轉換方法定義中的this指針了。上面函數中的arguments是調用時實際傳給function(){...}的參數數組,所以比如我要寫一個公開的Send(arg1,arg2,arg3),它里面有this指針,怎么辦呢?我先寫一個Pri_Send(arg1,arg2,arg3),然后在類定義(注意是類定義中)中這么寫:
this.Send = this.Pri_Send.Bind();
大功告成,現在可以盡情地在類的實例上使用Send方法了。而且對于那些參數個數不同或者根本沒有參數的方法,Bind()方法同樣適用,這都是arguments的功勞,呵呵。
此段代碼在IE6.0,FireFox1.0.7,opera 8.51下測試通過。應該沒有太大的平臺問題。
# ?re: JScript中的"this"關鍵字使用方式補充??回復??
@hzy"自定義類的定義中的this和該類的方法定義中的this不一樣",這句話我沒有明白,你是說的這種情況嗎?














# ?re: JScript中的"this"關鍵字使用方式補充??回復??
真的非常慚愧,今天早上我用了簡單的幾行代碼就證明我上面的結論是錯誤的。Bind()方法有用且正確,但在這里不需要,因為javascript并不像我昨天認為的那樣奇怪,兩個this的確指的是同一個類型實例。
經過仔細的審查(我自己的代碼),發現了這個錯誤觀點的根源。
雖然在birdhome兄所說的情況下,兩個this指的是同一個東西,但在某些特定情況下,卻并非如此。其根本原因還請birdhome兄多多指教。謝。
我的代碼中就有兩處這樣的情況。
我使用正流行的ajax設計了一個簡單的AsyncCommunicator類,用來提供更豐富的編程接口。其中兩個功能就是“更細粒度的事件處理”和“超時處理”。
AsyncCommunicator類包含一個XmlHttpRequest屬性,這個屬性本身就是一個XMLHttpRequest類的實例。因此在AsyncCommunicator類的定義中,可以如下寫法訪問這個實例:
this.XmlHttpRequest
現在,我要給AsyncCommunicator類定義更多事件,方法是利用XMLHttpRequest的onreadystatechange屬性。如何做呢?
我是這么寫的:
AsyncCommunicator{
this.XmlHttpObj= GetNewXmlHttpRequest();
this.XmlHttpObj.onreadystatechange = this.EventStateChangeHandler;
this.EventStateChangeHandler = function(){
switch(this.XmlHttpObj.readyState){ case 3: this.OnTransferBack(this); break; case 4: if (!this.Pri_Aborted){
this.Pri_HasReceived = true; this.OnReceive(this);
switch(parseInt(((this.XmlHttpObj.status).toString()).charAt(0))){ case 1: this.OnReceive1xx(this); break;
case 2:
this.OnReceive2xx(this);
break;
case 3: this.OnReceive3xx(this);
break;
case 4:
this.OnReceive4xx(this);
break;
case 5: this.OnReceive5xx(this);
break;
default:
break;
} //switch } //(!this.Pri_Aborted) break; default: break;
} //switch
} //function
} //AsyncCommunitor
注意這句this.XmlHttpObj.onreadystatechange = this.EventStateChangeHandler,這句就是加Bind()方法正確,不加就會出錯的源頭。經過思考,我覺得這個this,更確切一點說,函數的上下文,已經變了,不再是AsyncCommunitor,究竟是什么我也沒試出來,是否是XmlHttpObj?請birdhome兄指教。如果經過Bind方法轉換,把this賦給Bind()方法作參數,就可與更正這個問題。如下:
this.XmlHttpObj.onreadystatechange = this.EventStateChangeHandler.Bind(this);
我被這個問題折磨了一個晚上,因此一旦解決就緊接著犯了以偏概全的“左”傾錯誤,呵呵,把所有內部定義的方法都看成這種情況,因此每個方法都加了Bind()方法,想來真是慚愧,正如birdhome兄所言,通常情況下無須如此,因此已經改正。
還有第二個情況,就是
this.AbortWhenTimeOut方法的調用。這個方法定義如下。:
this.Ori_AbortWhenTimeOut = function(){ if (!this.Pri_HasReceived) { this.Pri_Aborted = true; this.OnTimeOut(this); this.Abort(); } }
在xmlhttprequest對象的open方法之后和send方法之前加入window.setTimeout方法(放在send之后就不行,奇怪,請指教),參數中就包括這個this.AbortWhenTimeOut方法,現在想來,很可能是window.setTimeout方法改變了this.AbortWhenTimeOut方法的上下文,導致該方法定義中的this指針的含義發生變化,因此不能正常執行。同樣,自從服用了Bind()之后,腰不酸了,背不痛了,腿也不抽筋了,走路也有勁了。呵呵。
關于上下文的這些奇妙變化(至少我覺得挺奇妙的,可能是受才智水平所限,慚愧),birdhome兄能否指教一二?
謝謝。
# ?re: JScript中的"this"關鍵字使用方式補充??回復??
PS,欽佩birdshome兄的專業水平。我隱約覺得javascript語言的諸多特性,是邏輯性很強的整體(其實事實也必然如此,用不著我去覺得,呵呵),例子舉的再多也只是“身在此山中”。遺憾我目前的水平還遠遠遠遠沒有達到可以跳出語言之外來論述語言的原理和本質。不知道birdshome兄是否有這個空閑和興趣對javascript語言做一個總結性發言,不只是how 更重要的是why。這只是個非常即興的提議,更多的是某種期待。無論如何,我希望我自己也能做到,someday。
這差不多應該也屬于技術區的非技術,呵呵,類似的話不會說第二次了。
以后只討論技術。
# ?re: JScript中的"this"關鍵字使用方式補充??回復??
@hzy????this.XmlHttpObj.onreadystatechange = this.EventStateChangeHandler后,EventStateChangeHandler方法里的this在事件觸發后是指的window對象。第二個使用window.setTimeout也是同樣的問題,使用setTimeout觸發的方法,其內部的this也是指的window。
????至于:對javascript語言做一個總結性發言。我不是很明白你具體指的內容,畢竟JavaScript這門大家認為"簡單"的小腳本,還是有挺多可圈點的內容,似乎很難簡單的就能"總結性"道來:(