讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議8:謹慎使用運算符 >

建議8:謹慎使用運算符

1.用===,而不用==

JavaScript有兩組相等運算符:===和!==、==和!=。===和!==這一組運算符會按照期望的方式工作。如果兩個運算數類型一致且擁有相同的值,那麼===返回true,而!==返回false。==和!=只有在兩個運算數類型一致時才會做出正確的判斷,如果兩個運算數是不同的類型,會試圖強制轉換運算數的類型。轉換的規則複雜且難以記憶,具體規則如下:


\'\'==\'0\'//false

0==\'\'//true

0==\'0\'//true

false==\'false\'//false

false==\'0\'//true

false==undefined//false

false==null//false

null==undefined//true


上面表達式如果全部使用===運算符,則都會返回true。

==和!=運算符缺乏傳遞性,需要引起警惕。所謂傳遞性就是:如果a==b為true,b==c為true,則a==c也為true。因此,在JavaScript開發中,建議永遠不要使用==和!=,而選用===和!==運算符。

下面分別介紹一下===和==運算符的算法。

(1)===運算符的算法

在使用===來判斷兩個值是否相等時,如判斷x===y,會先比較兩個值的類型是否相同,如果不相同,直接返回false。如果兩個值的類型相同,則接著根據x的類型展開不同的判斷邏輯:

❑如果x的類型是Undefined或Null,則返回true。

❑如果x的類型是Number,只要x或y中有一個值為NaN,就返回false;如果x和y的數字值相等,就返回true;如果x或y中有一個是+0,另外一個是-0,則返回true。

❑如果x的類型是String,當x和y的字符序列完全相同時返回true,否則返回false。

❑如果x的類型是Boolean,當x和y同為true或false時返回true,否則返回false。

❑當x和y引用相同的對象時返回true,否則返回false。

(2)==運算符的算法

在使用==來判斷兩個值是否相等時,如判斷x==y,如果x和y的類型一樣,判斷邏輯與===一樣;如果x和y的類型不一樣,==不是簡單地返回false,而是會進行一定的類型轉換。

❑如果x和y中有一個是null,另外一個是undefined,返回true,如null==undefined。

❑如果x和y中有一個類型是String,另外一個類型是Number,會將String類型的值轉換成Number來比較,如3==\"3\"。

❑如果x和y中有一個類型是Boolean,會將Boolean類型的值轉換成Number來比較,如true==1、true==\"1\"。

❑如果x和y中有一個類型是String或Number,另外一個類型是Object,會將Object類型的值轉換成基本類型來比較,如[3,4]==\"3,4\"。

2.謹慎使用++和--

遞增(++)和遞減(--)運算符使程序員可以用非常簡潔的風格去編碼,如在C語言中,它們使得通過一行代碼實現字符串的複製成為可能,例如:


for(p=src,q=dest;!*p;p++,q++)*q=*p;


事實上,這兩個運算符容易形成一種不謹慎的編程風格。大多數的緩衝區溢出錯誤所造成的安全漏洞都是由於這種編碼導致的。

當使用++和--時,代碼往往變得過於緊密、複雜和隱晦。因此,在JavaScript程序設計中不建議使用它們,從而使代碼風格變得更為整潔。

++和--運算符只能夠作用於變量、數組元素或對像屬性。下面的用法是錯誤的。


4++;


正確的用法如下:


var n=4;

n++;


++和--運算符的位置不同所得運算結果也不同。例如,下面的遞增運算符是先執行賦值運算,然後再執行遞加運算。


var n=4;

n++;//4


而下面的遞增運算符是先執行遞加運算,再進行賦值運算。


var n=4;

++n;


3.小心逗號運算符

逗號在JavaScript語言中表示連續運算,並返回最後運算的結果。例如,在下面這個示例中,JavaScript先運算第一個數值直接量,再運算第二個數值直接量,然後運算第三個數值直接量,最後運算第四個數值直接量,並返回最後一個運算值4。


var a=(1,2,3,4);

alert(a);//4


再如:


a=1,b=2,c=3;


等價於:


a=1;

b=2;

c=3;


作為運算符,逗號一般用在特殊環境中,即在只允許出現一個句子的地方,把幾個不同的表達式句子合併成一個長句。在JavaScript實際開發中,逗號運算符常與for循環語句聯合使用。例如,在下面這個簡單的for循環結構中,通過連續的運算符在參數表達式中運算多個表達式,以實現傳遞或運算多個變量或表達式。


for(var a=10,b=0;a>b;a++,b+=2){

document.write(\"a=\"+a+\"b=\"+b+\"<br>\");

}


逗號運算符比較「怪異」,稍不留心就會出錯。例如,在下面這個簡單的示例中,變量a的返回值為1,而不是連續運算後的返回值4。


a=1,2,3,4;

alert(a);//1


第一個數值1先賦值給變量a,然後a再參與連續運算,整個句子的返回值為4,而變量a的返回值為1,代碼演示如下:


alert((a=1,2,3,4));//4

alert(a=(1,2,3,4));//4


要確保變量a的值為連續運算,可以使用小括號邏輯分隔符強迫4個數值先進行連續運算,然後再賦值。


a=(1,2,3,4);

alert(a);//4


當使用var關鍵字來定義變量時,會發現a最終沒有返回值。


var a=1,2,3,4;

alert(a);//null


要確保var聲明的變量正確返回連續運算的值,需要使用小括號先強迫數值進行計算,最後再把連續運算的值賦值給變量a。


var a=(1,2,3,4);

alert(a);//4


4.警惕運算符的副作用

JavaScript運算符一般不會對運算數本身產生影響,如算術運算符、比較運算符、條件運算符、取逆、「位與」等。例如,a=b+c,其中的運算數b和c不會因為加法運算而導致自身的值發生變化。

但在JavaScript中還有一些運算符能夠改變運算數自身的值,如賦值、遞增、遞減運算等。由於這類運算符自身的值會發生變化,在使用時會具有一定的副作用,特別是在複雜表達式中,這種副作用更加明顯,因此在使用時應該時刻保持警惕。例如,在下面代碼中,變量a經過賦值運算和遞加運算後,其值發生了兩次變化。


var a;

a=0;

a++;

alert(a);//1


再如:


var a;

a=1;

a=(a++)+(++a)-(a++)-(++a);

alert(a);//-4


如果直觀地去判斷,會錯誤地認為返回值為0,實際上變量a在參與運算的過程中,變量a的值是不斷發生變化的。這種變化很容易被誤解。為了方便理解,進一步拆解(a++)+(++a)-(a++)-(++a)表達式:


var a;

a=1;

b=a++;

c=++a;

d=a++;

e=++a;

alert(b+c-d-e);//-4


如果表達式中還包含其他能夠引起自身值發生變化的運算,那麼整個表達式的邏輯就無法用人的直觀思維來描述了。因此,從代碼可讀性角度來考量:在一個表達式中不能夠對相同操作數執行兩次或多次引起自身值變化的運算,除非表達式必須這樣執行,否則應該避免產生歧義。這種歧義在不同編譯器中會產生完全不同的解析結果。例如,下面的代碼雖然看起來讓人頭疼,但由於每個運算數僅執行了一次引起自身值變化的運算,所以不會產生歧義。


a=(b++)+(++c)-(d++)-(++e);