JavaScript的成功得益於在正確的時間出現在正確的地點。 JavaScript的興起與瀏覽器的支援息息相關。你瞧,VBScript就沒這麼好運。
JavaScript很流行,但它有先天缺陷。 Brendan Eich當初只花了10天就把JavaScript設計出來了,作為JavaScript之父Brendan Eich如是說:
與其說我愛JavaScript,不如說我恨它。它是C語言和Self語言一夜情的產物。十八世紀英國文學家約翰遜博士說得好:「它的優秀之處並非原創,它的原創之處並不優秀。」
JavaScript的不足,最明顯之處是語法。
糟糕冗長的語法可選參數和預設值
function(a, b, option) {
optionoption = option || {};
// ... 參數,當沒有傳遞時,預設值是{}.然而,傳遞的option值有可能是假值(falsy值)。嚴格來寫,得如下判斷:
function(a, b, option) {
// ... = undefined ? option :{}也有可能是錯的,因為傳遞過來的可能就是undefined。當不需要b參數,刪除後,基於arguments.length 的判斷很容易導致忘記修改而出錯:
function(a, option) {
option = arguments.length > 2 ? option : {}; ..
}
function(a, b, option = {}) {
// ...
}
Let
for (var i=0, ilen=elements.length; i
LIB_addEventListener(element, click, function(event
LIB_addEventListener(element, click, i);
});
上面的程式碼常在面試題中出現,而解決方法是再包裹一層:
for (var i=0, ilen=elements.length; i
(function(num) {
LIB_addEventListener(element, click, function(event) {
如果直接支援let語法多好呀:
for (var i=0, ilen=elements.length; i
let (numnum = i let (numnum = i ) {
LIB_addEventListener(element, function(event) {
alert(I was originally number + num);
多好呀:
// private variables
var listeners = [];
}
export function clearEventListeners() {
listeners = [];
}
// ...
}
(function() {
繼承
JavaScript要透過原型鏈來實現繼承:
function Employee(first, last, position) {
// call the superclass constructor
Person.call(this, first, last);
this.position = position;
};
// inherit from Person
Employee.prototype = Object.create(Person.prototype);
EmployeeEmployee.prototype.constructor = Employee;
// define an overridding toString( // call superclasss overridden toString() method
return Person.prototype.toString.call(this) +
is a + this.position; is a + this.position;
}; is a + this.position;
};
class Employee extends Person {
constructor(first, last, position) {
super(first, last);
update(camera) {
return super.update() + is a + position;
}
}
感悟
ECMAScript委員會已意識到JavaScript在文法層面上的不足。在Harmony規範中,以上所有語法均已提案。
我們什麼時候才能使用以上文法呢?
Lisp語言的巨集特性非常強大。透過宏,你可以根據自己的喜好定義想要的語法格式。巨集特性使得Lisp成為一門「可程式化的程式語言(the programmable programming language)」.
JavaScript沒有巨集。為類C語言添加宏特性,目前依舊是個研究課題,很有難度。
Harmony規範裡的語法擴展,可能是我們所有人的夢。 Harmony有可能成為ECMAScript 6規範。在這之前,我們需要等待,耐心等待。
截止2011年5月,w3school顯示IE6的市佔率還有2.4%。 Net Market Share 顯示IE6佔有10.36%市佔率。還有IE7的市佔率也不少。這些老舊瀏覽器短期內不會退隱市場,對於商業公司來說,例如Amazon,不可能放棄這群用戶。糟糕的現狀。 (中國大陸更慘,IE6/7還佔有40%多市場份額)
死因:分號癌。 (semicolon cancer.作者的調侃,意指語法導致JavaScript死去)
透過上面的分析可以看出,宏特性實現太難,Harmony規範的實作則遙遙無期。大量程式設計師開始書寫JavaScript,其中有很多人已經厭倦或開始厭倦JavaScript冗長糟糕的語法。我們需要新的文法,我們不想等待! JavaScript,作為原始碼編寫語言,已經死了!
程式設計師喜歡掌控自己的命運。作為源碼編寫語言,JavaScript已死。我們可以選擇或創造另一種更好的原始碼語言,將其編譯成ECMAScript 3的語法格式。
JavaScript的新生,是作為編譯目標(compilation target)。
能編譯成JavaScript的語言很多。我在1997年時,收集過一份清單。包括:
JavaScript擴充語言:已死的 ECMAScript 4, Narrative Script, Objective-J.
已存在的語言:Scheme, Common Lisp, Smalltalk, Ruby, Python, Java, C#, Haskell等。
在這些編譯器專案中,Goggle的GWT Java-to-JavaScript編譯器有可能是最成功的一個。然而悲劇的是,現實項目中,很少看到GWT的身影。原因如下:
1.維護成本很高。編譯器可能有bug.假設你在一個大型專案中,發現了編譯器的一個bug,作為維護者,除了維護原始碼,你還得維護編譯器。天哪,你有這個本事嗎?你有這個本事,CEO也不願意花這個錢呀。
在上面的編譯器清單中,有一個非常有名的引起過很大轟動的:CoffeeScript。我們來談談它。
CoffeeScript
為什麼CoffeeScript如此火爆?我到現在為止也沒想明白。是因為給空白賦予了意義,還是帶箭頭的函數語法?每念及此,我的胃就忍不住波濤洶湧。 CoffeeScript有許多新特性:default parameter values, rest parameters, spread, destructuring,fixing the whole implied global mess… CoffeeScript很多特性是Harmony規範的一部分,有可能在未來瀏覽器中直接支援。 CoffeeScript 能讓人立刻滿足。
@pyronicide在Twitter上說:#coffeescript 支援函數預設參數值,這太令人興奮了。
在TXJS 2011大會上,Douglas Crockford 也表示:CoffeeScript無疑是個好東東。
CoffeeScript: Accelerated JavaScript Development 一書的作者說:
@trevorburnham
[...] CoffeeScript 不是將 JS 變成 Ruby 或 Python,而是透過一套語法,來更好地發揮JavaScript內在的優秀。
Douglas Crockford 認為 JavaScript 有好的方面,並開發了JSLint工具來保證開發者遠離JavaScript中的糟粕。 JSLint允許的語法子集值得擁有自己的名字,我們不妨稱之為GoodScript.
ECMAScript 5則引入了 "use strict" 指令來限制with等語法的使用。
CoffeeScript, GoodScript, ECMAScript 5的目標是一致的:遠離糟粕,同時提供有用的、安全的語言特性給你。
GoodScript沒有提供新特性,ECMAScript 5的嚴格模式,大部分瀏覽器還不支援。然而,我們不想等待。
剩下的選擇是CoffeeScript。好處:
特別適合web開發。這可能是其他JavaScript編譯器沒做或做得不好的地方。
CoffeeScript對JavaScript的封裝適度。這樣能使得編譯後的程式碼比較容易閱讀,調試也就不那麼困難了。
CoffeeScript看起來就像是書寫JavaScript程式碼的一套巨集。
CoffeeScript的編譯器提供客戶端版本。這樣,使用者可以自由選擇,開發者也可以快速開發新功能,而不受標準的限制。由社區的願景和需求推動CoffeeScript的發展,這很不錯。
發明自己的語言
你可以去做,這會是一個很好的練習。身為JavaScript編譯器的開發者,將擁有無上榮耀。
發明自己的語言,危險之處在於:你認為最終你將比JavaScript做得更好。語言設計很難,我敢打賭你的語言很難擴大市場佔有率。 CoffeeScript尚未進入青春期,就已經有抱怨的聲音了。
你可能會為自己的編譯器能編譯出簡單、可讀的程式碼而感到驕傲。可是,一碰到特殊狀況,你就會鬱悶得想撞牆。
你的語言裡將會出現慣用法。接著,你馬上會發現有人會破壞這些慣用法(除非你的語言剛好支持宏)。
風涼話就不多說了。立刻去開發自己的語言吧,你會成為一個很好的程式設計師。
作為編譯目標語言,JavaScript缺少什麼?
作為編譯目標語言,JavaScript重獲新生。在 JSConf.US talk中,Brendan Eich表示:Harmony規格的目的是讓JavaScript成為更好的編譯目標。
編譯後的JavaScript有可能比手寫的JavaScript運作效率更低,這就和編譯後的C有可能比手寫的組合語言效率更低一樣。幸運的是,JavaScript的瓶頸主要在DOM操作上,語言本身的效率損耗相對可以接受。雖然話是這麼說,但在一些高效的源碼語言編譯後,由於JavaScript本身的問題,可能極其低效,以致於無法在真實環境中使用。 Harmony規範中已經有部分特性能確保避免這類問題。
合理的尾部調用
function isEven(number) {
if (number === 0) {
return number - 1);
}
}
function isOdd( number) {
if (number === 0) {
return false;
}
}
isEven(100000); // InternalError: too much recursion
上面的程式碼,在目前的瀏覽器中運行,會堆疊溢位。
可以透過彈跳床(trampolines)技巧來優化:
function bounce(ret) {
while (typeof ret === function) {
retret = ret(); . number) {
if (number === 0) {
return true;
}
else {
};
}
}
function isOdd(number) {
if (number === 0) {
return false;
}
else {
return function() { return };
}
}
bounce(function() {return isEven(100000);}); // true
透過bounce 方式,在執行isOdd(99999)時,isEven(100000)已經完成並從堆疊中退出了,因此不會造成溢位。
幸運地是,ECMAScript Harmony已經考慮到了這一點,會自動進行最佳化。這對程式開發者和編譯器開發者都是有益的。
Lambdas
lambda並不神奇。簡言之,lambda就是可調用的東西,例如function,但需要遵守TCP(Tennent一致性原則,Tennent’s Correspondence Principle)。 TCP要求:用一個緊鄰的lambda對表達式或程式碼區塊進行封裝,不會改變被封裝的程式碼的意義。
很明顯,JavaScript 的function 不是lambda:
function one() {
}
one();
}
one();
(function() { return 1;
}());
}
one(); , b| a + b}
function one() {
({|| );
one(); // 1
lambda 塊的稻草人提案目前還沒有提升到Harmony 規範中,讓我們一起努力吧。
瀏覽器缺少什麼?
聽說Webkit的小伙子們也在做同樣的事情,可惜我找不到任何證據了。
通曉數種語言
JavaScript在瀏覽器上的壟斷,表示前端程式設計師都會同一門語言。然而,編譯器的差異性,會讓CoffeeScript程式設計師,很難立刻看懂基於Traceur的JavaScript程式碼。
這種分歧不可避免。例如有C,同時有C++和Objective-C等各種語言。 Java也一樣,基於JVM還可以選擇Clojure或JRuby。微軟意識到這一點,開發CLR.C#,Basic,IronPython等都可以運行在CLR上。
擁有多種原始碼書寫語言會增加社群的溝通障礙。程式設計師仍需要了解JavaScript.至少一段時間內程式設計師還需要懂得JavaScript。但在短短幾年後,他們可能會更了解其他源碼語言。
總結
能有機會目睹JavaScript的新生,是件很棒的事。在JavaScript編譯的競爭中,很難說誰會最終贏得市場份額,但毫無疑問,這肯定會很有趣。如今,CoffeeScript蓄勢待發,但我相信許多其他成功的源碼語言將接踵而至。