Javascript 中的類和閉包
2024-05-06 14:11:56
供稿:網友
有人說javascript也是面向對象的,只是它是prototype based,當然這只是概念上的區別,我不想討論js是不是面向對象的,關鍵是想說明雖然javascript的類表現得很像其他語言中的類,但是內部的實現機理確不太一致,如果一味的把javascript中的類類比作其他語言中的類,有時候腦子會犯混。
先看一段簡單的代碼,一般教材上介紹如何新建一個類的時候都是這樣的(當然還有更復雜的方法,不過本質上是一樣的):
代碼如下:
function MyClass(x) {
this.x = x;
}
var obj = new MyClass('Hello class');
alert(obj.x);
毫無疑問,此時obj具有一個x屬性,現在的值是 Hello class. 但是,obj到底是什么?MyClass僅僅是一個函數而已,我們稱之為構造函數。在其他OO的語言中,構造函數是要放在class關鍵字內部的,也就是先要聲明一個類。另外,函數體內的this又是什么?其他OO語言中,this的概念是很明確的,就是當前對象,因為它在構造函數執行之前已經聲明了類,類的內部的一些字段都是已經定義好的。
先解釋下,在javascript的函數中,this關鍵字表示的是調用該函數的作用域(scope),作用域的概念也不是太好理解,下面再解釋。不過可以簡單的認為它是調用函數的對象。再看MyClass函數,它內部的this是什么呢?
如果我們把代碼改成:
代碼如下:
var obj = MyClass('Hello class');
這是完全合乎語法的。如果這段代碼是在瀏覽器中運行的,調試一下可以發現,this是window對象。而和obj沒有任何關系,obj還是undefined,alert也不會有結果。原來的代碼之所以可以工作,都是new關鍵字的功勞。new關鍵字把一個普通的函數變成了構造函數。也就是說,MyClass還是一個普通的函數,它之所以能構造出一個obj,基本上是new的功勞。當函數之前有new關鍵字的時候,javascript會創造一個匿名對象,并且把當前函數的作用域設置為這個匿名對象。然后在那個函數內部引用this的話就是引用的這個匿名對象,最后,即使這個函數沒有return,它也會把這個匿名對象返回出去。那么obj自然就具有了x屬性。
現在這個MyClass已經有點像一個類了。但是,這并不是new的工作的全部。Javascript同樣可以方便的實現繼承——依靠是prototype.prototype也是一個對象,畢竟除了原始類型,所有的東西都是對象,包括函數。更為重要的是,前面提到javascript是prototype based,它的含義就是在javascript中沒有類的概念,類是不存在的,一個函數,它之所以表現的像類,就是靠的prototype. prototype可以有各種屬性,也包括函數。上一段說的new在構造對象的過程中,在最終返回那個匿名對象之前,還會把那個函數的prototype中的屬性一一復制給這個對象。這里的復制是復制的引用,而不是新建的一個對象,把內容復制過來,在其內部,相當于保留了一個構造它的函數的prototype的引用。有些教材含糊的說所有的“所有對象都有一個prototype屬性”,這種說法是不確切的,雖然它內部確實有一個prototype屬性,但是對外是不可見的。只有函數對象是有prototype屬性的,函數對象的prototype默認有一個constructor屬性。