您好,登錄后才能下訂單哦!
本篇內容介紹了“JavaScript面向對象的支持怎么實現”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在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中的實現
------
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('
for ', objectArr[i], '
');
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], '
');
}
document.writeln('
');
}
一個常常被開發者忽略的事實是: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.', '
');
}
else {
document.write('call a method, by object: ', this.name, '
');
}
}
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, '
');
document.writeln('window.value: ', window.value, '
');
你不能指望這樣的代碼在調用結束后,會使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()也存在一些開銷,這使得它不適合于頻繁變動的查找集。最后,(我想你可能已經注意到了)使用對象來查找的時候并不能準確定位到查找數據,而數組中可以指向結果的下標。
5). 使用instanceof關鍵字的運算
------
在JavaScript中提供了instanceof關鍵字來檢測實例的類型。這在前面討論它的“五重身份”時已經講過。但instanceof的問題是,它總是列舉整個原型鏈以檢測類型(關于原型繼承的原理在“構造與析構”小節講述),如:
//---------------------------------------------------------
// instanceof使用中的問題
//---------------------------------------------------------
function MyObject() {
// ...
}
function MyObject2() {
// ...
}
MyObject2.prototype = new MyObject();
obj1 = new MyObject();
obj2 = new MyObject2();
document.writeln(obj1 instanceof MyObject, '
');
document.writeln(obj2 instanceof MyObject, '
');
我們看到,obj1與obj2都是MyObject的實例,但他們是不同的構造函數產生的。??注意,這在面向對象理論中正確的:因為obj2是MyObject的子類實例,因此它具有與obj1相同的特性。在應用中這是obj2的多態性的體現之一。
但是,即便如此,我們也必須面臨這樣的問題:如何知道obj2與obj1是否是相同類型的實例呢???也就是說,連構造器都相同?
instanceof關鍵字不提供這樣的機制。一個提供實現這種檢測的能力的,是Object.constructor屬性。??但請先記住,它的使用遠比你想象的要難。
好的,問題先到這里。constructor屬性已經涉及到“構造與析構”的問題,這個我們后面再講。“原型繼承”、“構造與析構”是JavaScript的OOP中的主要問題、核心問題,以及“致命問題”。
6). null與undefined
------
在JavaScript中,null與undefined曾一度使我迷惑。下面的文字,有利于
你更清晰的認知它(或者讓你更迷惑):
- null是關鍵字;undefined是Global對象的一個屬性。
- null是對象(空對象, 沒有任何屬性和方法);undefined是undefined類
型的值。試試下面的代碼:
document.writeln(typeof null);
document.writeln(typeof undefined);
- 對象模型中,所有的對象都是Object或其子類的實例,但null對象例外:
document.writeln(null instanceof Object);
- null“等值(==)”于undefined,但不“全等值(===)”于undefined:
document.writeln(null == undefined);
document.writeln(null == undefined);
- 運算時null與undefined都可以被類型轉換為false,但不等值于false:
document.writeln(!null, !undefined);
document.writeln(null==false);
document.writeln(undefined==false);
[@more@]
“JavaScript面向對象的支持怎么實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。