原文:http://www.nczonline.net/blog/2012/10/16/does-javascript-need-classes/
譯者注:在我長達一年的工作生涯中,我遇到過有人把建構函式稱做類,還有人把對象字面量稱做類.這比把Firefox擴充叫成外掛程式都令我*疼.下面是Brendan Eich給今年的jsconf.eu錄製的視頻,其中提到了類.
無論你喜歡還是不喜歡,ECMAScript 6中將會包含類(class)這個新東西了[1].在JavaScript中,對類的需求一直都有兩極分化的趨勢.有些人特別喜歡JavaScript中沒有類,因為這和其他語言不同.另一方面,還有一些人厭惡JavaScript沒有類,因為這和其他語言不同.那些從C++或Java轉到JavaScript的人們最需要克服的一個心理障礙就是"JavaScript中沒有類",有些人和我說過,這就是他們為什麼不喜歡Javascript或者不繼續深入學習Javascript的原因之一.
JavaScript從發明的那天起就沒有真正的類,這使得從一開始就造成了混亂.有不少JavaScript書籍或文章中都講到了類,就好像JavaScript中真的存在類一樣.但其實,他們所說的類只是一些自訂的建構函式,這些函數可以用來構造一些自訂的參考型別.在JavaScript中,參考型別已經是最接近於類的東西了.下面的代碼你應該已經很熟悉了:
function MyCustomType(value) { this.property = value;}MyCustomType.prototype.method = function() { return this.property;};
很多時候,這種代碼被解釋為是聲明了一個MyCustomType
類.但實際上,該代碼做的所有事情僅僅是聲明了一個MyCustomType
函數,配合new運算子可以用它建立一個參考型別
MyCustomType的執行個體
.該函數和其他的函數並沒有什麼本質的不同.因為其他自訂的函數也可以作為建構函式來使用.
這樣的代碼從表面上看起來根本不像是在定義一個類,被定義的建構函式和其原型對象上的方法似乎沒有什麼聯絡.如果是JavaScript新手,很可能會認為這是兩段完全無關的代碼.但實際上,這兩段代碼有非常緊密的聯絡,只是和其他語言中的類的寫法相差甚遠罷了.
更令人困惑的是,一旦談到繼承,大部分人想到的術語是子類和超類等等,這樣的概念只有在存在真正的類的前提下才有意義.在JavaScript中,實現繼承的代碼同樣是冗長的:
function Animal(name) { this.name = name;}Animal.prototype.sayName = function() { console.log(this.name);};function Dog(name) { Animal.call(this, name);}Dog.prototype = new Animal(null);Dog.prototype.bark = function() { console.log("Woof!");};
實現繼承需要兩個步驟,建立建構函式和重寫原型對象,這樣的代碼非常混亂.
在第一版的《JavaScript進階程式設計》中,我使用過術語"類".但從我收到的反饋中看,這樣寫是很令人困惑的.所以在第二版中,我把所有的"類(class)"都替換成了"類型(type)".事實表明,使用"類型"這個術語可以減少很多的混亂.
可是,還有一個比較突出的問題:定義一個自訂類型的文法是冗長的,實現兩個類型之間的繼承會更加複雜.沒有什麼簡單的方法可以調用一個屬於超類型的方法.在目前看來,建立並管理一個自訂類型是件很痛苦的事,如果你不信,可以看看有下面有多少JavaScript架構使用了自己的定義自訂類型和繼承的方法:
- YUI – 用
Y.extend()來實現繼承
.使用該方法會在子類型上添加一個"superclass"屬性.
[2]
- Prototype – 用
Class.create()和
Object.extend()來處理對象和"類"
.[3]
- Dojo – 使用
dojo.declare()和
dojo.extend()
.[4]
- MooTools – 有一個自訂類型
Class,可以用來定義和擴充"類"
.[5]
這麼多的JavaScript架構都有自己的解決方案,這明顯是非常混亂的.JavaScript開發人員們需要一種更好的實現此功能的文法.
ECMAScript 6中的類其實並沒有什麼新東西,只是在你已經熟悉的模式上增加了一層文法糖.看看下面這個例子:
class MyCustomType { constructor(value) { this.property = value; } method() { return this.property; }}
這個ECMAScript 6中的類定義其實就是本文上面那個MyCustomType例子的另一種寫法.使用這種類建立的對象執行個體完全和使用建構函式建立的對象執行個體一樣.唯一的區別就是前者擁有更緊湊的文法.下面看看繼承的寫法:
class Animal { constructor(name) { this.name = name; } sayName() { console.log(this.name); }}class Dog extends Animal { constructor(name) { super(name); } bark() { console.log("Woof!"); }}
在實際效果上這個例子也同樣等同於前面那種繼承的寫法.只是複雜的實現步驟被一個簡單的extends關鍵字代替了
.在類定義中你還可以直接使用super(),無需明確指出超類型的建構函式
.
ECMAScript 6中的類是基於你已經熟知的JavaScript模式.實現繼承的原理還和以前一樣(基於原型鏈,調用超類型的建構函式),方法添加在原型上,屬性在建構函式中聲明.真正的區別只有一個:你可以打更少的字.
所以,如果你現在仍然不贊同ECMAScript 6引入類這麼個東西,你可以這麼想,要引入的這個類不是什麼新東西,也並沒有從根本上改變JavaScript的工作機制.不過我個人更推薦使用關鍵字type而不是
class
.
那麼JavaScript真的需要類嗎?答案是不需要,但JavaScript的確需要一個更簡潔的方法來建立自訂類型.這恰巧就是ECMAScript 6中的"類"正要做的.如果這個"類"能協助來自其他語言的開發人員們更容易的轉向JavaScript,那麼它就是好東西.
參考
- Maximally minimal classes (ECMA)
- YUI extend() (YUILibrary)
- Prototype Classes and Inheritance (Prototype)
- Creating and Enhancing Dojo Classes (SitePen)
- MooTools Class (MooTools)