[譯]JavaScript需要類嗎?


原文:http://www.nczonline.net/blog/2012/10/16/does-javascript-need-classes/


譯者注:在我長達一年的工作生涯中,我遇到過有人把構造函數稱做類,還有人把對象字面量稱做類.這比把火狐擴展叫成插件都令我*疼.下面是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,那么它就是好東西.

 

參考

  1. Maximally minimal classes (ECMA)
  2. YUI extend() (YUILibrary)
  3. Prototype Classes and Inheritance (Prototype)
  4. Creating and Enhancing Dojo Classes (SitePen)
  5. MooTools Class (MooTools)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM