這幾天,我在讀《javascript語言精粹》。
這本書很薄,100多頁,正好假日裡翻翻。
該書的作者是douglas crockford,他是目前世界上最精通javascript的人之一,也是json格式的創造者。
他認為javascript有很多糟粕。因為1995年brendan eich設計這種語言的時候,隻用了三個月,很多語言特性沒有經過深思熟慮,就推向了市場。結果等到人們意識到這些問題的時候,已經有100萬程式員在使用它了,不可能再大幅修改語言本身了。是以,douglas crockford決定,他要告訴大家,javascript中哪些部分是精粹,哪些部分是糟粕和雞肋。
這個想法非常好,但是我不得不說,這本書寫得不夠好,不适合新手閱讀。原因如下:1)douglas crockford叙述得不清晰,更像與同行讨論問題,而不是由淺入深地講解問題。這本書的重點不是解釋,是以讀完後,我覺得javascript好像變得更複雜了。2)他固執地使用鐵路圖(railroad diagram)解釋每一條語句。全世界似乎隻有他一個人使用這種比javascript更難看懂的圖。3)該書基本上是一本簡化的javascript文法手冊,缺乏足夠的新内容。4)該書舉例過少,而且在最難的函數和對象部分,使用的例子都是環環相套、層層遞進的例子,導緻閱讀起來很吃力。
該書最有價值的内容不是正文,反而是附錄。在附錄b中,douglas crockford列出了12種應該避免使用的javascript文法,我覺得非常值得推廣。
==============================
1. ==
javascript有兩組相等運算符,一組是==和!=,另一組是===和!==。前者隻比較值的相等,後者除了值以外,還比較類型是否相同。
請盡量不要使用前一組,永遠隻使用===和!==。因為==預設會進行類型轉換,規則十分難記。如果你不相信的話,請回答下面五個判斷式的值是true還是false:
false == 'false'
false == undefined
false == null
null == undefined
0 == ''
前三個是false,後兩個是true。
2. with
with的本意是減少鍵盤輸入。比如
obj.a = obj.b;
obj.c = obj.d;
可以簡寫成
with(obj) {
a = b;
c = d;
}
但是,在實際運作時,解釋器會首先判斷obj.b和obj.d是否存在,如果不存在的話,再判斷全局變量b和d是否存在。這樣就導緻了低效率,而且可能會導緻意外,是以最好不要使用with語句。
3. eval
eval用來直接執行一個字元串。這條語句也是不應該使用的,因為它有性能和安全性的問題,并且使得代碼更難閱讀。
eval能夠做到的事情,不用它也能做到。比如
eval("myvalue = myobject." + mykey + ";");
可以直接寫成
myvalue = myobject[mykey];
4. continue
這條指令的作用是傳回到循環的頭部,但是循環本來就會傳回到頭部。是以通過适當的構造,完全可以避免使用這條指令,使得效率得到改善。
5. switch 貫穿
switch結構中的case語句,預設是順序執行,除非遇到break,return和throw。有的程式員喜歡利用這個特點,比如
switch(n) {
case 1:
case 2:
break;
這樣寫容易出錯,而且難以發現。是以建議避免switch貫穿,凡是有case的地方,一律加上break。
6. 單行的塊結構
if、while、do和for,都是塊結構語句,但是也可以接受單行指令。比如
if (ok) t = true;
甚至寫成
if (ok)
t = true;
這樣不利于閱讀代碼,而且将來添加語句時非常容易出錯。建議不管是否隻有一行指令,都一律加上大括号。
if (ok){
7. ++和--
遞增運算符++和遞減運算符--,直接來自c語言,表面上可以讓代碼變得很緊湊,但是實際上會讓代碼看上去更複雜和更晦澀。是以為了代碼的整潔性和易讀性,不用為好。
8. 位運算符
javascript完全套用了java的位運算符,包括按位與&、按位或|、按位異或^、按位非~、左移<<、帶符号的右移>>和用0補足的右移>>>。
這套運算符針對的是整數,是以對javascript完全無用,因為javascript内部,所有數字都儲存為雙精度浮點數。如果使用它們的話,javascript不得不将運算數先轉為整數,然後再進行運算,這樣就降低了速度。而且"按位與運算符"&同"邏輯與運算符"&&,很容易混淆。
9. function語句
在javascript中定義一個函數,有兩種寫法:
function foo() { }
和
var foo = function () { }
兩種寫法完全等價。但是在解析的時候,前一種寫法會被解析器自動提升到代碼的頭部,是以違背了函數應該先定義後使用的要求,是以建議定義函數時,全部采用後一種寫法。
10. 基本資料類型的包裝對象
javascript的基本資料類型包括字元串、數字、布爾值,它們都有對應的包裝對象string、number和boolean。是以,有人會這樣定義相關值:
new string("hello world");
new number(2000);
new boolean(false);
這樣寫完全沒有必要,而且非常費解,是以建議不要使用。
另外,new object和new array也不建議使用,可以用{}和[]代替。
11. new語句
javascript是世界上第一個被大量使用的支援lambda函數的語言,本質上屬于與lisp同類的函數式程式設計語言。但是目前世界,90%以上的程式員都是使用面向對象程式設計。為了靠近主流,javascript做出了妥協,采納了類的概念,允許根據類生成對象。
類是這樣定義的:
var cat = function (name) {
this.name = name;
this.saying = 'meow' ;
然後,再生成一個對象
var mycat = new cat('mimi');
這種利用函數生成類、利用new生成對象的文法,其實非常奇怪,一點都不符合直覺。而且,使用的時候,很容易忘記加上new,就會變成執行函數,然後莫名其妙多出幾個全局變量。是以,建議不要這樣建立對象,而采用一種變通方法。
douglas crockford給出了一個函數:
object.beget = function (o) {
var f = function (o) {};
f.prototype = o ;
return new f;
};
建立對象時就利用這個函數,對原型對象進行操作:
var cat = {
name:'',
saying:'meow'
var mycat = object.beget(cat);
對象生成後,可以自行對相關屬性進行指派:
mycat.name = 'mimi';
12. void
在大多數語言中,void都是一種類型,表示沒有值。但是在javascript中,void是一個運算符,接受一個運算數,并傳回undefined。
void 0; // undefined
這個指令沒什麼用,而且很令人困惑,建議避免使用。