本章 提供運算子的概觀。
所有運算子 強制轉換 (如 強制轉換 中所述) 其運算元為適當的類型。大多數運算子僅適用於基本值 (例如,算術運算子與比較運算子)。這表示在對物件執行任何操作之前,會將其轉換為基本值。一個不幸的範例是加號運算子,許多語言將其用於陣列串接。然而,JavaScript 並非如此,此運算子會將陣列轉換為字串並附加它們:
> [1, 2] + [3] '1,23' > String([1, 2]) '1,2' > String([3]) '3'
無法在 JavaScript 中覆載或自訂運算子,甚至等於運算子也不行。
有數種方式可以使用 純粹賦值運算子:
x = 值
x
var x = 值
obj.propKey = 值
obj['propKey'] = 值
arr[index] = 值
賦值是一種會評估為已賦值值的表達式。這允許您串接賦值。例如,下列陳述句會將 0
賦值給 y
和 x
x
=
y
=
0
;
一個 複合賦值運算子 寫成 op=
,其中 op
是 幾個二元運算子之一,而 =
是賦值運算子。下列兩個表達式是等效的:
myvar
op
=
value
myvar
=
myvar
op
value
換句話說,複合賦值運算子 op=
會將 op
套用於兩個運算元,並將結果賦值給第一個運算元。讓我們來看一個使用加號運算子 (+
) 透過複合賦值的範例
> var x = 2; > x += 3 5 > x 5
以下是所有 複合賦值運算子:
*=
、/=
、%=
、+=
、-=
<<=
、>>=
、>>>=
、&=
、^=
、|=
+=
JavaScript 有兩種方法可判斷兩個值是否相等:
===
)和嚴格不相等(!==
)僅將類型相同的兩個值視為相等。 ==
)和不相等(!=
)會嘗試在比較不同類型值之前,先將其轉換為與嚴格(不)相等相同的類型。 寬鬆相等在兩個方面有問題。首先,它執行轉換的方式令人困惑。其次,由於運算子過於寬鬆,類型錯誤可能會隱藏更久。
務必使用嚴格相等,並避免使用寬鬆相等。只有在您想知道為什麼應該避免使用寬鬆相等時,才需要了解它。
相等無法自訂。運算子無法在 JavaScript 中重載,您也無法自訂相等運作的方式。在某些運算中,您通常需要影響比較,例如 Array.prototype.sort()
(請參閱排序和反轉元素(具破壞性))。該方法選擇性地接受一個會執行陣列元素之間所有比較的回呼函式。
類型不同的值絕不會嚴格相等。如果兩個值類型相同,則會成立下列斷言:
undefined === undefined
null === null
兩個數字
x
===
x
// unless x is NaN
+
0
===
-
0
NaN
!==
NaN
// read explanation that follows
兩個物件(包括陣列和函式):x === y
唯且僅當 x
和 y
是同一個物件;亦即,如果您想比較不同的物件,您必須實作自己的比較演算法:
> var b = {}, c = {}; > b === c false > b === b true
透過正常等式比較演算法運作方式如下。如果兩個運算元有相同的類型(六種規格類型之一—未定義、Null、布林、數字、字串和物件),則透過嚴格等式比較它們。
否則,如果運算元是
undefined
和 null
,則它們被視為寬鬆相等
> undefined == null true
否則—如果上述情況都不適用—寬鬆比較的結果為 false
。
步驟 3 表示等式和轉換為布林(請參閱轉換為布林)運作方式不同。如果轉換為布林,大於 1 的數字會變成 true
(例如,在 if
陳述式中)。但那些數字並不會寬鬆等於 true
。註解說明如何計算結果
> 2 == true // 2 === 1 false > 2 == false // 2 === 0 false > 1 == true // 1 === 1 true > 0 == false // 0 === 0 true
類似地,雖然空字串等於 false
,但並非所有非空字串都等於 true
:
> '' == false // 0 === 0 true > '1' == true // 1 === 1 true > '2' == true // 2 === 1 false > 'abc' == true // NaN === 1 false
某些寬鬆性可能很有用,視乎你的需要而定:
> 'abc' == new String('abc') // 'abc' == 'abc' true > '123' == 123 // 123 === 123 true
由於 JavaScript 將字串轉換為數字的方式(請參閱轉換為數字),其他情況會出現問題
> '\n\t123\r ' == 123 // usually not OK true > '' == 0 // 0 === 0 true
如果你比較一個物件與一個非物件,它會轉換為一個原始值,這會導致奇怪的結果:
> {} == '[object Object]' true > ['123'] == 123 true > [] == 0 true
然而,只有兩個物件是同一個物件時,它們才相等。這表示你不能真正比較兩個包裝器物件
> new Boolean(true) === new Boolean(true) false > new Number(123) === new Number(123) false > new String('abc') == new String('abc') false
你有時會讀到寬鬆相等(==
)的有效使用案例。此部分會列出它們並指出更好的替代方案。
下列比較確保 x
既不是 undefined
也不是 null
:
if
(
x
!=
null
)
...
雖然這是撰寫此檢查的簡潔方式,但它會讓初學者感到困惑,而專家無法確定它是否為錯字。因此,如果你想檢查 x
是否有值,請使用真值標準檢查(在真值和假值中涵蓋)
if
(
x
)
...
如果你想更精確,你應該對兩個值執行明確檢查
if
(
x
!==
undefined
&&
x
!==
null
)
...
如果你不確定值 x
是數字還是數字字串,你可以使用下列檢查:
if
(
x
==
123
)
...
前述檢查 x
是否為 123
或 '123'
。同樣地,這非常簡潔,但最好明確說明
if
(
Number
(
x
)
===
123
)
...
寬鬆相等讓你比較原始值與包裝原始值:
> 'abc' == new String('abc') true
有三個理由反對這種方法。首先,寬鬆相等不適用於包裝原始值之間:
> new String('abc') == new String('abc') false
其次,你應該避免使用包裝器。第三,如果你確實使用它們,最好明確說明
if
(
wrapped
.
valueOf
()
===
'abc'
)
...
JavaScript 知道下列排序運算子:
<
)
<=
)
>
)
>=
)
這些運算子適用於 數字和字串:
> 7 >= 5 true > 'apple' < 'orange' true
對於字串,它們不太有用,因為它們區分大小寫,而且無法妥善處理重音符號等功能(有關詳細資訊,請參閱 比較字串)。
您可以評估比較
x
<
y
透過執行 下列步驟:
obj
會透過內部運算 ToPrimitive(obj, Number)
轉換為基本型別(請參閱 演算法: ToPrimitive()—將值轉換為基本型別),它會呼叫 obj.valueOf()
和 obj.toString()
(如果需要)來執行此動作。
其他排序運算子以類似的方式處理。
粗略來說,加號運算子 會檢查其運算元。如果其中一個運算元是字串,另一個運算元也會轉換為字串,且兩個運算元會串接:
> 'foo' + 3 'foo3' > 3 + 'foo' '3foo' > 'Colors: ' + [ 'red', 'green', 'blue' ] 'Colors: red,green,blue'
否則,兩個運算元都會轉換為數字(請參閱 轉換為數字),並相加
> 3 + 1 4 > 3 + true 4
這表示您評估的順序很重要
> 'foo' + (1 + 2) 'foo3' > ('foo' + 1) + 2 'foo12'
您可以評估加法
value1
+
value2
透過執行下列步驟
obj
會透過內部運算 ToPrimitive(obj)
轉換為基本型別(請參閱 演算法: ToPrimitive()—將值轉換為基本型別),它會呼叫 obj.valueOf()
和 obj.toString()
(如果需要)來執行此動作。對於日期,會先呼叫 obj.toString()
。
下列 運算子只有單一型別的運算元,而且也會產生該型別的結果。它們在其他地方有說明。
布林值運算子
二元邏輯 運算子(請參閱 二元邏輯運算子:And (&&) 和 Or (||))
x
&&
y
,
x
||
y
!
x
數字運算子
在此,我們將回顧特殊運算子,即條件運算子、逗號運算子,以及 void
運算子。
條件 運算子是一個表達式:
«
condition
»
?
«
if_true
»
:
«
if_false
»
如果條件為 true
,則結果為 if_true
;否則,結果為 if_false
。例如
var
x
=
(
obj
?
obj
.
prop
:
null
);
運算子周圍的括號不是必需的,但它們使閱讀更輕鬆。
«
left
»
,
«
right
»
逗號運算子評估兩個 運算元,並傳回 right
的結果。大致上,它對表達式所做的,與分號對陳述式所做的相同。
此範例說明第二個運算元變為運算子的結果
> 123, 'abc' 'abc'
此範例說明兩個運算元都已評估
> var x = 0; > var y = (x++, 10); > x 1 > y 10
逗號運算子會令人困惑。最好不要耍小聰明,只要有可能,就寫兩個獨立的陳述式。
void
運算子的語法 為:
void
«
expr
»
它會評估 expr
並傳回 undefined
。以下是一些範例
> void 0 undefined > void (0) undefined > void 4+7 // same as (void 4)+7 NaN > void (4+7) undefined > var x; > x = 3 3 > void (x = 5) undefined > x 5
因此,如果您將 void
實作為一個函式,它看起來如下
function
myVoid
(
expr
)
{
return
undefined
;
}
void
運算子與其運算元緊密相關,因此請視需要使用括號。例如, void 4+7
會繫結為 (void 4)+7
。
在 ECMAScript 5 中,void
很少會用到。它的主要使用案例是:
void 0
作為 undefined
的同義詞
undefined
在 ECMAScript 5 中不太可能被變更,這使得這個使用案例變得不那麼重要(有關詳細資訊,請參閱 變更 undefined)。
在某些情況下,傳回 undefined
而不是表達式的結果非常重要。這時可以透過 void
來捨棄該結果。其中一個情況涉及 javascript:
URL,這應該避免用於連結,但對書籤小工具很有用。當您造訪其中一個 URL 時,許多瀏覽器會以評估 URL「內容」的結果取代目前的文件,但前提是結果不是 undefined
。因此,如果您想要開啟一個新視窗,而不變更目前顯示的內容,可以執行下列動作:
javascript
:
void
window
.
open
(
"http://example.com/"
)
void
字首(請參閱 IIFE 變化:字首運算子)。[11]
根據 JavaScript 創造者布蘭登·艾克,他將它加入語言中,以協助處理 javascript:
連結(上述使用案例之一):
我在 Netscape 2 發布之前,將
void
運算子加入 JS,以便在 javascript: URL 中輕鬆捨棄任何非 undefined 的值。[12]
如果您想要 分類一個值,很不幸地,您必須在 JavaScript 中區分基本型別和物件(請參閱 第 8 章)
typeof
運算子區分基本型別和物件,並判斷基本型別的型別。
instanceof
運算子判斷一個物件是否為特定建構函式的執行個體。請參閱 第 17 章,以取得有關 JavaScript 中物件導向程式設計的更多資訊。
typeof
«
value
»
傳回描述 value
為何種值的字串。以下是一些範例
> typeof undefined 'undefined' > typeof 'abc' 'string' > typeof {} 'object' > typeof [] 'object'
typeof
用於區分基本型別和物件,以及分類基本型別(無法由 instanceof
處理)。很遺憾地,此運算子的結果並非完全合理,而且僅與 ECMAScript 規格的型別(在 JavaScript 的型別 中說明)大致對應
運算元 | 結果 |
|
|
|
|
布林值 |
|
數字值 |
|
字串值 |
|
函式 |
|
所有其他一般值 |
|
(引擎建立的值) | JavaScript 引擎可以建立 |
很遺憾地,typeof null
為 'object'
。這被視為錯誤(null
不是內部型別 Object 的成員),但無法修正,因為這樣做會破壞現有的程式碼。因此您必須小心 null
。例如,下列函式會檢查 value
是否為物件:
function
isObject
(
value
)
{
return
(
value
!==
null
&&
(
typeof
value
===
'object'
||
typeof
value
===
'function'
));
}
試用
> isObject(123) false > isObject(null) false > isObject({}) true
第一個 JavaScript 引擎將 JavaScript 值表示為 32 位元字。此類字的最低 3 位元用作型別標籤,以表示值是物件、整數、雙精度浮點數、字串或布林值(如您所見,即使是這個早期的引擎,也會盡可能將數字儲存為整數)。
物件的型別標籤為 000。為了表示值 null
,引擎使用了機器語言 NULL 指標,一個所有位元都為零的字。typeof
檢查型別標籤以判斷值的型別,這就是它會回報 null
為物件的原因。[13]
typeof
x
===
'undefined'
有兩個用例
x
是否為 undefined
。
x
是否存在。
以下是這兩種使用案例的範例
> var foo; > typeof foo === 'undefined' true > typeof undeclaredVariable === 'undefined' true
對於第一個使用案例,通常比較好的選擇是直接與 undefined
進行比較。但是,它不適用於第二個使用案例
> var foo; > foo === undefined true > undeclaredVariable === undefined ReferenceError: undeclaredVariable is not defined
instanceof
營運子:
«
value
»
instanceof
«
Constr
»
決定 value
是否已由建構函式 Constr
或子建構函式建立。以下是一些範例
> {} instanceof Object true > [] instanceof Array // constructor of [] true > [] instanceof Object // super-constructor of [] true
正如預期的那樣,對於非值 undefined
和 null
,instanceof
為 false
> undefined instanceof Object false > null instanceof Object false
但對於所有其他原始值,它也是 false
> 'abc' instanceof Object false > 123 instanceof Object false
有關 instanceof
的詳細資訊,請參閱 instanceof 營運子。
new
(請參閱 第 3 層:建構函式—執行個體工廠)
new Point(3, 5)
delete
(請參閱 刪除屬性)
delete obj.prop
in
(請參閱 屬性的反覆運算和偵測)
'prop' in obj