JS—感悟JavaScript中的面向對象

JavaScript是一種基於對象(object-based)的語言,我們遇到的所有東西幾乎都是對象。由於JavaScript是一種基於原型(prototype)的面向對象的語言,沒有類的概念,所有的一切都派生自現有對象的一個副本,所以說JavaScript又不是一種真正的面向對象編程的語言。

    JavaScript中的對象分為兩種:
一種是稱為"普通對象"的Object對象,也就是我們普遍理解的那些:數字、日期、用戶自定義對象(如:{})等等。
另一種是被稱為"方法對象"的Function對象。你可能覺得奇怪:方法就是方法,怎麼成瞭對象瞭?但是在JavaScript中,方法的確是被當做對象來處理的。下面是一個簡單的例子:
[javascript] 
1. <span style="font-family:FangSong_GB2312;font-size:18px;">function func() {alert('Hello!');}    
2. alert(func.toString()); </span> 
在上面這個例子中,func雖然是作為一個方法定義的,但它自身卻包含一個toString方法,說明func在這裡是被當作一個對象來處理的。更準確的說,func是一個"方法對象"。接著做例子:
[javascript]
1. func.name="I am func"; 
2. alert(func.name); 
我們可以任意的為func設置屬性,這更加說明瞭func是一個對象。那麼方法對象和普通對象的區別在哪裡?首先方法對象當然是可執行的,在它後面加上一對括號,就是執行這個方法對象瞭。
[javascript]
1. func(); 
所以,方法對象具有二重性。一方面它可以執行,另一方面它完全可以被當成一個普通對象來使用。這意味著什麼呢?這意味著方法對象是可以完全獨立於其他對象存在的。這一點我們可以和java比較一下:在java中,方法必須在某一個類中定義,不能單獨存在;而JavaScript中則不需要。
方法對象獨立於其他方法,就意味著它能被任意的引用和傳遞。繼續,看下面的例子
[javascript]
1. function invoke(f){ 
2.     f(); 
3. } 
4. invoke(func); 

將一個方法對象func傳遞給另一個方法對象invoke,讓後者在適當的時候執行func.這就是所謂的"回調"瞭。另外,方法對象的這種特殊性,也使得this關鍵字不容易把握。
方法對象除瞭可以被執行,另一個特殊的功用是它可以通過new關鍵字來創建普通對象。
話說每一個方法對象被創建時,都會自動的擁有一個叫 prototype 的屬性。這個屬性並無什麼特別之處,它和其他的屬性一樣可以訪問,可以賦值。不過當我們用 new 關鍵字來創建一個對象的時候,prototype 就起作用瞭:它的值(也是一個對象)所包含的所有屬性,都會被復制到新創建的那個對象上去。下面是一個例子:
[javascript] 
1. func.prototype.name="prototype of func"; 
2. var f=new func(); 
3. alert(f.name); 
執行過程中會彈出兩個對話框:'prototype of func '和 'Hello!'兩個對話框。後一個對話框表示 f 這個新創建的對象從func.prototype那裡拷貝瞭name屬性;而前一個對話框表示func被作為方法執行瞭一遍。你可能會問,為什麼這個時候還要把func執行一遍呢?其實這個時候執行func,就是起"構造函數"的作用。為瞭形象的說明,我們再做一個例子:
[javascript] 
1. function func(){ 
2.     this.name="name has been changed"; 
3. } 
4. func.prototype.name="prototype of func"; 
5. var f=new func(); 
6. alert(f.name); 
這時候會發現f的name屬性不再是'prototype of func',而是被替換成瞭'name has been changed'.這就是func這個對象方法所起到的'構造函數'的作用。
這裡需要說明一點:之所以這裡會彈出構造函數中的name,是因為對象尋找屬性時,如果自己沒有這個屬性,就會在構造方法的prototype所指向/引用的對象中找,看能否找到同名屬性,如果找到同名屬性,則直接使用。
<之所以對象能夠執行prototype 屬性定義的方法,是因為用構造方法生成的對象和構造方法之間有緊密聯系,對象尋找屬性時,如果自己沒有這個屬性,會在構造方法的propotype所指向/引用的對象中找,看能否找到同名屬性,如果找到,就會讀取它的值並返回.(這個過程會持續向上,直到持續到Object對象為止,即所謂原型方式的繼承).> 
==========================================================================
在JavaScript中,用new關鍵字創建對象是執行瞭下面三個步驟: <這一點需要商榷>
1. 創建一個新的普通對象;
2. 將方法對象的 prototype 屬性的所有屬性復制到新的普通對象中去。
3. 以新的普通對象作為上下文來執行方法對象。
==========================================================================
對於“new func()”這樣的語句,可以描述為“從 func 創建一個新對象”。總之,prototype 這個屬性的唯一特殊之處,就是在創建新對象的時候瞭。

那麼我們就可以利用這一點。比如有兩個方法對象 A 和 B,既然從 A 創建的新對象包含瞭所有 A.prototype 的屬性,那麼我將它賦給 B.prototype,那麼從 B 創建的新對象不也有同樣的屬性瞭?寫成代碼就是這樣:
[javascript] 
1. var A =new Function(); 
2. var B =new Function(); 
3. A.prototype.hello = function(){alert('Hello!');}    
4. B.prototype = new A();    
5. new B().hello();  

這就是 JavaScript 的所謂“繼承”瞭,其實質就是屬性的拷貝,這裡利用瞭 prototype 來實現。如果不用 prototype,那就用循環瞭,效果是一樣的。所謂“多重繼承”,自然就是到處拷貝瞭。

JavaScript 中面向對象的原理,暫時就理解成這樣子。 JavaScript 沒有“類”這個東西,面向對象可以沒有類嗎?當然可以。先有類,然後再有對象,這本來就不合理,因為類本來是從對象中歸納出來的,先有對象再有類,這才合理。像下面這樣的:
[javascript] 
1. var obj={};  //我發現瞭一個東東。 
2. obj.eat=function(){return "I am eating."}; //我發現它會吃; 
3. obj.sleep=function(){return "ZZZzzz…"}; //我發現它會睡; 
4. obj.talk=function(){return "Hi!"}; //我發現它會說話; 
5. obj.think=function(){return "Hmmm…"} //我發現它還會思考。 
6.  
7. var Human=new Function(); //我決定給它起名叫"人"。 
8. Human.prototype=obj; //這個東西就代表瞭所有"人"的概念。 
9.  
10. var h=new Human();  //當我發現其他同它一樣的東西, 
11. alert(h.talk()); //我就知道它也是"人"瞭! 

 

作者:mazhaojuan

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *