不只是加減乘除的運算子


對於運算子的使用,最主要是要注意自動型態轉換的問題,其中有關 +-*/ 以及 =====!=!==)的說明,在〈弱型別的代價〉有先作過一些說明。

+ 兩邊的運算元有一個是字串時,就會作字串的串接,而整個運算式中有數字也有字串時,要注意運算順序。例如:

> 1 + 2 + '3';
'33'
> '1' + 2 + 3;
'123'
> '1' + (2 + 3);
'15'
> 

第一個式子是先作 1 + 2 的運算,結果再與 '3' 作字串的串接,得到 '33' 的字串;第二個式子是先作字串的串接,得到 '12',再與 3 作字串的串接,得到 '123' 的字串;不確定的話,可如第三個式子,使用 () 定義優先運算順序。

在比較運算上,除了〈弱型別的代價〉中提到的使用 =====!=!== 之外,還可以使用 >>=<<=,在 JavaScript 中,這些運算子不僅可用在比較數字,還可以用來比較字串,如果運算子兩邊都是字串,則逐字元依 Unicode 碼點進行比較。

但如果 >>=<<= 兩邊一個是字串,一個是數字,那麼型態轉換又有可能自動發生。如果字串的部份代表數字,則會轉換為數字,再與另一數字進行比較。如果字串的部份不代表數字,則會被轉換為 NaN,與另一數字比較的結果自然就是 false

>>=<<= 兩邊其實可用於物件,比較結果視物件的 valueOf 方法內容而定,例如傳回數字,則以傳回的數字相比。例如 Date 就是一個例子:

> var date = new Date();
undefined
> date.valueOf();
1397699040389
> date > 1397699040389;
false
> date > 1397699040388;
true
> date > new Date();
false
> new Date() > date;
true
> var obj = {
...     valueOf : function() {
.....       return 100;
.....   }
... };
undefined
> 1 + obj;
101
> 1 > obj;
false
> 

正如上例所示,其他運算式若需轉為數值,也可以透過 valueOf 取得值,預設 valueOf 會傳回實例本身。如果你沒有重新定義 valueOf,就會使用實例本身作比較。

> var o1 = {};
undefined
> o1.valueOf();
{}
> o1 === o1.valueOf();
true
> 

注意,=== 用來比較物件時,是比較是否參考同一物件;== 得留意兩邊的運算元,是否有型態轉換後再進行比較的可能性。

如果你要測試某個物件上是否有某個特性,且要傳回 truefalse 的結果,則可以使用 in 運算子。例如:

> 'x' in {x : 10};
true
>

之後還會詳細介紹物件,在這邊也可以看到,物件上的特性名稱,實際上是個字串。

如果你想要得知某個物件是否由哪個建構式(Constructor,或所謂的類別,雖然類別這名詞並不精確)所產生,則可以使用 instanceof 運算式。例如:

> [] instanceof Array;
true
> [] instanceof Object;
true
> [] instanceof Date;
false
> 

對於沒有繼承關係的,instanceof 會傳回 false

typeof 也常用來測試物件型態,傳回值是字串,對於基本資料型態,數值會傳回 'number'、字串會傳回 'string'、布林會傳回 'boolean'、對於 Function 實例會傳回 'function'、對於 undefined 會傳回 'undefined'、對於其他物件一律傳回 'object',包括 null 也是傳回 'object'

> typeof 1;
'number'
> typeof '';
'string'
> typeof true;
'boolean'
> typeof function() {};
'function'
> typeof undefined;
'undefined'
> typeof {};
'object'
> typeof null;
'object'
> 

&&|| 大家都知道用來判斷 AND 與 OR 關係是否成立,而且具有捷徑運算,例如 && 只要左運算元可以判斷為不成立,則 && 結果直接不成立,不用判斷右運算元,而 || 只要左運算元判斷為成立,就直接判斷 || 結果為成立,不用再判斷右運算元。

不過,&&|| 其傳回值並非 truefalse ,而是在判斷式整個可以確認是否成立時,傳回當時的運算元。例如:

> 'left' && 'right';
'right'
> 0 && 'right';
0
> 'left' && 0;
0
> 

在上面第一個例子中,左運算元非空字串,會當作結果成立,所以再判斷右運算元,也非空字串,所以判斷整個 && 成立,由於是停在第二個運算元,所以傳回 'right'。第二個例子,由於 0 會被當作不成立,此時不用判斷右運算元,就可判斷整個 && 運算不成立,所以直接傳回 0。 第三個例子,由於左運算元非空字串,會當作結果成立,所以再判斷右運算元為 0,所以當作不成立,整個 && 運算此時確認不成立,傳回右運算元。

|| 的例子則如下:

> 'right' || 'left';
'right'
> 0 || 'left';
'left'
> 'right' || 0;
'right'
> 

這個特性很有用,例如,想要在某個值存在時直接使用,而不存在時提供預設值,則可以如下:

function doSome(arg) {
    var option = arg || 'default';
    return option;
}

console.log(doSome());              // default
console.log(doSome('caterpillar')); // caterpillar

上面用到了函式作為範例,之後還會詳細說明,像是如果函式參數很多時,還可以合併物件的方式提供預設值。上例其實就相當於使用 ?: 運算子的結果:

function doSome(arg) {
    var option = arg ? arg : 'default';
    return option;
}

console.log(doSome());              // default
console.log(doSome('caterpillar')); // caterpillar

兩者可以達到相同的效果,不過慣例上還是使用 || 的方式。

void 運算子放在任何資料或運算式之前,都會產生 undefined,這是取得 undefined 的另一個方式,在一些無法直接在程式中寫 undefined 來取得 undefined 值的環境中,可以使用 void 來取得。例如:

> void 0;
undefined
> typeof void 0;
'undefined'
>