本章では演算子の概要を説明します。
すべての演算子は(型強制で説明されているように)オペランドを適切な型に強制変換します。ほとんどの演算子はプリミティブ値(算術演算子や比較演算子など)でのみ機能します。つまり、オブジェクトは処理される前にプリミティブに変換されます。これが不都合な例として、多くの言語で配列の連結に使用されているプラス演算子が挙げられます。しかし、JavaScriptではこの演算子は配列を文字列に変換して連結します。
> [1, 2] + [3] '1,23' > String([1, 2]) '1,2' > String([3]) '3'
JavaScriptでは、等価演算子でさえ、演算子をオーバーロードしたり、カスタマイズしたりすることはできません。
単純な代入演算子を使用する方法はいくつかあります。
x = value
x
に代入します。var x = value
obj.propKey = value
obj['propKey'] = value
arr[index] = value
代入は、代入された値を評価する式です。これにより、代入を連鎖させることができます。たとえば、次のステートメントは0
をy
とx
の両方に代入します。
x
=
y
=
0
;
複合代入演算子はop=
のように記述され、ここでop
はいくつかの二項演算子の1つであり、=
は代入演算子です。次の2つの式は同等です。
myvar
op
=
value
myvar
=
myvar
op
value
言い換えると、複合代入演算子op=
は両方のオペランドにop
を適用し、結果を最初のオペランドに代入します。複合代入を使用してプラス演算子(+
)を使用する例を見てみましょう。
> var x = 2; > x += 3 5 > x 5
以下はすべて複合代入演算子です。
*=
、/=
、%=
、+=
、-=
<<=
、>>=
、>>>=
、&=
、^=
、|=
+=
JavaScriptには、2つの値が等しいかどうかを判断する2つの方法があります。
===
)と厳密な不等価性(!==
)は同じ型を持つ値のみを等しいとみなします。==
)と不等価性(!=
)は、厳密な(不)等価性と同様に、比較する前に異なる型の値を変換しようとします。寛大な等価性は、2つの点で問題があります。まず、変換の実行方法がわかりにくいことです。第二に、演算子が非常に寛容であるため、型エラーが長く隠れたままになる可能性があります。
常に厳密な等価性を使用し、寛大な等価性は避けてください。後者について学ぶ必要があるのは、なぜ避けるべきかを知りたい場合のみです。
等価性はカスタマイズできません。JavaScriptでは演算子をオーバーロードすることはできず、等価性の動作をカスタマイズすることもできません。比較に影響を与える必要がある操作がいくつかあります。たとえば、Array.prototype.sort()
(要素のソートと反転(破壊的)参照)です。このメソッドは、配列要素間のすべての比較を実行するコールバックをオプションで受け入れます。
異なる型の値は、厳密には決して等しくありません。両方の値が同じ型を持つ場合、次のアサーションが成り立ちます。
undefined === undefined
null === null
2つの数値
x
===
x
// unless x is NaN
+
0
===
-
0
NaN
!==
NaN
// read explanation that follows
2つのオブジェクト(配列と関数を含む):x === y
は、x
とy
が同じオブジェクトである場合、かつその場合に限り成り立ちます。つまり、異なるオブジェクトを比較する場合は、独自の比較アルゴリズムを実装する必要があります。
> var b = {}, c = {}; > b === c false > b === b true
特殊な数値NaN
(NaN参照)は自身と等しくありません。
> NaN === NaN false
したがって、落とし穴:値がNaNかどうかを確認するで説明されている他の方法を使用して確認する必要があります。
通常の等価性による比較のアルゴリズムは、次のとおりです。両方のオペランドが同じ型(6つの仕様型のいずれか—Undefined、Null、Boolean、Number、String、およびObject)を持つ場合、厳密な等価性を使用して比較します。
そうでない場合、オペランドが
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
ただし、2つのオブジェクトは、同じオブジェクトである場合にのみ等しくなります。つまり、2つのラッパーオブジェクトを実際に比較することはできません。
> 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
このアプローチには3つの理由があります。まず、寛大な等価性は、ラップされたプリミティブ間では機能しません。
> 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
の結果を返します。おおよそ、式に対してセミコロンが文に対して行うことを行います。
この例は、2番目のオペランドが演算子の結果になることを示しています。
> 123, 'abc' 'abc'
この例は、両方のオペランドが評価されることを示しています。
> var x = 0; > var y = (x++, 10); > x 1 > y 10
コンマ演算子は分かりにくいものです。賢くならず、できる限り2つの個別の文を書く方が良いでしょう。
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
はほとんど役に立ちません。主な使用例は次のとおりです。
undefined
の同義語としてのvoid 0
undefined
はECMAScript 5では変更される可能性が低いので、この使用例はそれほど重要ではありません(詳細については、undefinedの変更を参照)。いくつかの状況では、式の結果ではなくundefined
を返すことが重要です。その場合、void
を使用してその結果を破棄できます。そのような状況の1つはjavascript:
URLに関連します。これはリンクには避けるべきですが、ブックマークレットには便利です。これらのURLのいずれかを訪問すると、多くのブラウザは、結果がundefined
でない場合にのみ、現在のドキュメントをURLの「コンテンツ」の評価結果で置き換えます。したがって、現在表示されているコンテンツを変更せずに新しいウィンドウを開きたい場合は、次のように実行できます。
javascript
:
void
window
.
open
(
"http://example.com/"
)
void
をプレフィックスすることです(IIFEのバリエーション:プレフィックス演算子を参照)。[11]JavaScriptの作成者であるBrendan Eichによると、彼はjavascript:
リンク(前述の使用例の1つ)を支援するために言語に追加しました。
javascript: URLで定義されていない以外の値を簡単に破棄できるように、Netscape 2の出荷前にvoid演算子をJSに追加しました。[12]
値を分類したい場合、残念ながらJavaScriptではプリミティブとオブジェクトを区別する必要があります(第8章を参照)。
typeof
演算子は、プリミティブとオブジェクトを区別し、プリミティブの型を決定します。instanceof
演算子は、オブジェクトが特定のコンストラクタのインスタンスかどうかを決定します。JavaScriptにおけるオブジェクト指向プログラミングの詳細については、第17章を参照してください。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'
には2つの使用例があります。
x
がundefined
かどうかを判断します。x
が存在するかどうかを判断します。両方の使用例の例を以下に示します。
> var foo; > typeof foo === 'undefined' true > typeof undeclaredVariable === 'undefined' true
最初の使用例では、undefined
と直接比較する方が通常は良い選択肢です。ただし、2番目の使用例では機能しません。
> 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
予想通り、instanceof
は、非値undefined
とnull
ではfalse
です。
> undefined instanceof Object false > null instanceof Object false
しかし、他のすべてのプリミティブ値でもfalse
です。
> 'abc' instanceof Object false > 123 instanceof Object false
instanceof
の詳細については、instanceof演算子を参照してください。
次の3つの演算子は、オブジェクトで動作します。これについては、別の場所で説明します。
new
(レイヤー3:コンストラクタ—インスタンスのファクトリを参照)new Point(3, 5)
delete
(プロパティの削除を参照)delete obj.prop
in
演算子(「プロパティの反復処理と検出」を参照)'prop' in obj