私たちは学校出身の多くのオペレーターを知っています。それらは、加算+
、乗算*
、減算-
などです。
この章では、単純な演算子から始めて、次に学校の算数ではカバーされない JavaScript 固有の側面に焦点を当てます。
次に進む前に、一般的な用語をいくつか理解しましょう。
オペランド– 演算子が適用されるものです。たとえば、 5 * 2
の乗算には 2 つのオペランドがあります。左のオペランドは5
で、右のオペランドは2
です。場合によっては、これらのことを「オペランド」ではなく「引数」と呼ぶことがあります。
オペランドが 1 つある場合、演算子は単項になります。たとえば、単項否定-
数値の符号を反転します。
x = 1 とします。 x = -x; アラート( x ); // -1、単項否定が適用されました
演算子に 2 つのオペランドがある場合、演算子は2 項になります。同じマイナスがバイナリ形式でも存在します。
x = 1、y = 3 とします。 アラート( y - x ); // 2、バイナリマイナスで値を減算します
正式には、上記の例には、同じ記号を共有する 2 つの異なる演算子があります。つまり、符号を反転する単項演算子である否定演算子と、ある数値から別の数値を減算する二項演算子である減算演算子です。
次の数学演算がサポートされています。
加算+
、
減算-
、
乗算*
、
分割/
、
残りの%
、
べき乗**
。
最初の 4 つは簡単ですが、 %
と**
については少し説明が必要です。
剰余演算子%
、その見た目に反して、パーセントとは関係ありません。
a % b
の結果は、 a
をb
で割った整数の余りです。
例えば:
アラート( 5 % 2 ); // 5 を 2 で割った余りを 1 アラート( 8 % 3 ); // 8 を 3 で割った余りを 2 アラート( 8 % 4 ); // 0、8 を 4 で割った余り
べき乗演算子a ** b
、 a
のb
乗を計算します。
学校の数学では、これを a bと書きます。
例えば:
アラート( 2 ** 2 ); // 2² = 4 アラート( 2 ** 3 ); // 23 = 8 アラート( 2 ** 4 ); // 2⁴ = 16
数学と同様に、べき乗演算子は非整数に対しても定義されます。
たとえば、平方根は 1/2 の累乗です。
アラート( 4 ** (1/2) ); // 2 (1/2 乗は平方根と同じ) アラート( 8 ** (1/3) ); // 2 (1/3 乗は立方根と同じ)
学校の算数を超えた JavaScript 演算子の機能を見てみましょう。
通常、プラス演算子+
数値を合計します。
ただし、バイナリ+
文字列に適用されると、文字列がマージ (連結) されます。
let s = "私の" + "文字列"; アラート; // mystring
いずれかのオペランドが文字列である場合、他のオペランドも文字列に変換されることに注意してください。
例えば:
アラート( '1' + 2 ); // 「12」 アラート( 2 + '1' ); // 「21」
最初のオペランドが文字列であるか 2 番目のオペランドであるかは関係ありません。
より複雑な例を次に示します。
アラート(2 + 2 + '1' ); // 「221」ではなく「41」
ここではオペレーターが次々と作業を行っていく。最初の+
2 つの数値を合計するので4
を返し、次の+
文字列1
を追加するので、 4 + '1' = '41'
のようになります。
アラート('1' + 2 + 2); // 「14」ではなく「122」
ここで、最初のオペランドは文字列であり、コンパイラは他の 2 つのオペランドも文字列として扱います。 2
'1'
に連結されるため、 '1' + 2 = "12"
および"12" + 2 = "122"
のようになります。
バイナリ+
は、このような方法で文字列をサポートする唯一の演算子です。他の算術演算子は数値のみを処理し、常にオペランドを数値に変換します。
減算と除算のデモは次のとおりです。
アラート( 6 - '2' ); // 4、「2」を数値に変換します アラート( '6' / '2' ); // 3、両方のオペランドを数値に変換します
プラス+
には、上で使用したバイナリ形式と単項形式の 2 つの形式があります。
単項プラス、つまり単一の値に適用されるプラス演算子+
、数値に対しては何も行いません。ただし、オペランドが数値でない場合は、単項プラスによって数値に変換されます。
例えば:
// 数値には影響しません x = 1 とします。 アラート( +x ); // 1 y = -2 とします。 アラート( +y ); // -2 // 数値以外を変換します アラート( +true ); // 1 アラート( +"" ); // 0
実際にはNumber(...)
と同じことを行いますが、より短くなります。
文字列を数値に変換する必要が非常に頻繁に発生します。たとえば、HTML フォーム フィールドから値を取得する場合、通常は文字列です。それらを合計したい場合はどうすればよいでしょうか?
バイナリプラスはそれらを文字列として追加します。
リンゴ = "2" とします。 オレンジ = "3" とします。 アラート(リンゴ + オレンジ); // "23"、バイナリプラスは文字列を連結します
それらを数値として扱いたい場合は、変換してから合計する必要があります。
リンゴ = "2" とします。 オレンジ = "3" とします。 // 両方の値がバイナリプラスの前に数値に変換されます アラート( +リンゴ + +オレンジ ); // 5 // 長いバージョン //alert( 数値(リンゴ) + 数値(オレンジ) ); // 5
数学者の観点からすると、プラスがたくさんあるのは奇妙に見えるかもしれません。しかし、プログラマの観点から見ると、特別なことは何もありません。最初に単項プラスが適用され、文字列が数値に変換され、次にバイナリプラスがそれらを合計します。
単項プラスがバイナリプラスよりも先に値に適用されるのはなぜですか?これから見ていきますが、これは優先順位が高いためです。
式に複数の演算子がある場合、実行順序は演算子の優先順位、つまり演算子のデフォルトの優先順位によって定義されます。
私たちは学校で、式1 + 2 * 2
の乗算を加算の前に計算する必要があることを知っています。まさにそれが先行事項です。乗算は加算よりも優先順位が高いと言われます。
括弧は優先順位をオーバーライドするため、デフォルトの順序に満足できない場合は、括弧を使用して順序を変更できます。たとえば、 (1 + 2) * 2
と書きます。
JavaScript には多くの演算子があります。すべての演算子には、対応する優先順位番号があります。数値が大きい方が最初に実行されます。優先順位が同じ場合、実行順序は左から右になります。
以下は優先順位表からの抜粋です (これを覚えておく必要はありませんが、単項演算子は対応する二項演算子よりも高いことに注意してください)。
優先順位 | 名前 | サイン |
---|---|---|
… | … | … |
14 | 単項プラス | + |
14 | 単項否定 | - |
13 | べき乗 | ** |
12 | 乗算 | * |
12 | 分割 | / |
11 | 追加 | + |
11 | 引き算 | - |
… | … | … |
2 | 割り当て | = |
… | … | … |
ご覧のとおり、「単項プラス」の優先順位は14
で、「加算」(バイナリー プラス) の11
よりも高くなります。そのため、式"+apples + +oranges"
では、加算の前に単項プラスが機能します。
代入=
も演算子であることに注意してください。優先順位テーブルでは、非常に低い優先順位2
でリストされています。
そのため、 x = 2 * 2 + 1
のように変数を代入すると、最初に計算が行われ、次に=
が評価され、結果がx
に保存されます。
x = 2 * 2 + 1 とします。 アラート( x ); // 5
=
が「魔法の」言語構造ではなく演算子であるという事実には、興味深い意味があります。
JavaScript のすべての演算子は値を返します。これは+
と-
の場合は明らかですが、 =
の場合にも当てはまります。
x = value
呼び出しは、 value
x
に書き込み、それを返します。
以下は、より複雑な式の一部として代入を使用するデモです。
a = 1 とします。 b = 2 とします。 c = 3 - (a = b + 1) とします。 アラート( a ); // 3 アラート( c ); // 0
上の例では、式(a = b + 1)
の結果は、 a
に割り当てられた値 (つまり3
) です。その後、さらなる評価に使用されます。
面白いコードですね。 JavaScript ライブラリで時々見かけるため、それがどのように機能するかを理解する必要があります。
ただし、そのようなコードは書かないでください。このようなトリックでは、コードが明確になったり、読みやすくなったりすることはありません。
もう 1 つの興味深い機能は、割り当てをチェーンする機能です。
a、b、c とします。 a = b = c = 2 + 2; アラート( a ); // 4 アラート( b ); // 4 アラート( c ); // 4
連鎖された割り当ては右から左に評価されます。まず、右端の式2 + 2
が評価され、左側の変数c
、 b
およびa
に割り当てられます。最終的に、すべての変数は 1 つの値を共有します。
繰り返しになりますが、読みやすくするために、このようなコードは数行に分割することをお勧めします。
c = 2 + 2; b = c; a = c;
これにより、特にコードを素早く目で確認するときに、読みやすくなります。
多くの場合、演算子を変数に適用し、新しい結果を同じ変数に保存する必要があります。
例えば:
n = 2 とします。 n = n + 5; n = n * 2;
この表記は、演算子+=
および*=
使用して短縮できます。
n = 2 とします。 n += 5; // 現在は n = 7 (n = n + 5 と同じ) n *= 2; // 現在は n = 14 (n = n * 2 と同じ) アラート( n ); // 14
すべての算術演算子およびビット演算子には、短い「変更と代入」演算子が存在します: /=
、 -=
など。
このような演算子は通常の代入と同じ優先順位を持つため、他のほとんどの計算の後に実行されます。
n = 2 とします。 n *= 3 + 5; // n *= 8 と同じように、右側の部分が最初に評価されます アラート( n ); // 16
数値を 1 ずつ増加または減少させることは、最も一般的な数値演算の 1 つです。
したがって、これには特別な演算子があります。
Increment ++
は変数を 1 ずつ増やします。
カウンタ = 2 とします。 カウンタ++; // counter = counter + 1 と同じように動作しますが、より短くなります。 アラート(カウンター); // 3
デクリメント--
変数を 1 ずつ減らします。
カウンタ = 2 とします。 カウンタ - ; // counter = counter - 1 と同じように動作しますが、より短くなります。 アラート(カウンター); // 1
重要:
インクリメント/デクリメントは変数にのみ適用できます。 5++
のような値で使用しようとすると、エラーが発生します。
演算子++
および--
、変数の前または後に配置できます。
演算子が変数の後に続く場合、それは「後置形式」: counter++
になります。
「プレフィックス形式」は、演算子が変数の前に置かれる形式です: ++counter
。
これらのステートメントはどちらも同じことを行います。つまり、 counter
1
ずつ増やします。
違いはありますか?はい、ただし、 ++/--
の戻り値を使用する場合にのみ確認できます。
明確にしましょう。ご存知のとおり、すべての演算子は値を返します。インクリメント/デクリメントも例外ではありません。接頭辞形式は新しい値を返しますが、接尾辞形式は古い値 (増分/減分前) を返します。
違いを確認するために、次の例を示します。
カウンタ = 1 とします。 a = ++カウンターとします。 // (*) アラート(a); // 2
行(*)
では、接頭辞形式++counter
counter
をインクリメントし、新しい値2
を返します。したがって、 alert
2
表示されます。
次に、後置形式を使用してみましょう。
カウンタ = 1 とします。 a = counter++; // (*) ++counter を counter++ に変更しました アラート(a); // 1
行(*)
では、後置形式counter++
もcounter
をインクリメントしますが、古い値 (インクリメント前の) を返します。したがって、 alert
1
表示されます。
要約すると:
インクリメント/デクリメントの結果が使用されない場合、どの形式を使用するかに違いはありません。
カウンタ = 0 とします。 カウンタ++; ++カウンター; アラート(カウンター); // 2、上記の行は同じことを行っています
値を増やして演算子の結果をすぐに使用したい場合は、接頭辞形式が必要です。
カウンタ = 0 とします。 アラート(++カウンター); // 1
値をインクリメントしたいが、以前の値を使用したい場合は、接尾辞形式が必要です。
カウンタ = 0 とします。 アラート(カウンター++); // 0
他の演算子間のインクリメント/デクリメント
演算子++/--
は式内でも使用できます。これらの優先順位は、他のほとんどの算術演算よりも高くなります。
例えば:
カウンタ = 1 とします。 アラート( 2 * ++counter ); // 4
以下と比較してください:
カウンタ = 1 とします。 アラート( 2 * counter++ ); // 2、counter++ が「古い」値を返すため
技術的には問題ありませんが、このような表記法を使用すると、通常、コードが読みにくくなります。 1 行で複数のことを行うのは良くありません。
コードを読んでいるときに、高速で「垂直」に目をスキャンすると、 counter++
のようなものを簡単に見逃す可能性があり、変数が増加したことは明らかではありません。
「1 行 1 アクション」のスタイルをお勧めします。
カウンタ = 1 とします。 アラート( 2 * カウンタ ); カウンタ++;
ビット演算子は引数を 32 ビット整数として扱い、バイナリ表現のレベルで動作します。
これらの演算子は JavaScript 固有のものではありません。これらはほとんどのプログラミング言語でサポートされています。
演算子のリスト:
そして ( &
)
または ( |
)
XOR ( ^
)
いいえ ( ~
)
左シフト ( <<
)
右シフト ( >>
)
ゼロフィル右シフト ( >>>
)
これらの演算子は、最も低い (ビットごとの) レベルで数値を操作する必要がある場合に、ほとんど使用されません。 Web 開発ではこれらの演算子はほとんど使用されないため、これらの演算子はすぐには必要なくなりますが、暗号化などの一部の特殊な領域では便利です。必要に応じて、MDN のビットごとの演算子の章を読むことができます。
カンマ演算子 は,
最もまれで珍しい演算子の 1 つです。場合によっては、より短いコードを記述するために使用されるため、何が起こっているかを理解するためにそれを知る必要があります。
カンマ演算子を使用すると、複数の式をコンマ,
で区切って評価できます。それぞれが評価されますが、最後の結果のみが返されます。
例えば:
a = (1 + 2, 3 + 4) とします。 アラート( a ); // 7 (3 + 4 の結果)
ここでは、最初の式1 + 2
が評価され、その結果は破棄されます。次に、 3 + 4
が評価され、結果として返されます。
カンマの優先順位は非常に低いです
カンマ演算子の優先順位は=
よりも非常に低いため、上記の例では括弧が重要であることに注意してください。
それらがない場合: a = 1 + 2, 3 + 4
最初に+
を評価し、数値を合計してa = 3, 7
にし、次に代入演算子=
によってa = 3
を割り当て、残りは無視されます。 (a = 1 + 2), 3 + 4
のようなものです。
最後の式以外をすべて破棄する演算子がなぜ必要なのでしょうか?
場合によっては、より複雑な構成でこれを使用して、複数のアクションを 1 行に入れることがあります。
例えば:
// 1 行に 3 つの操作 for (a = 1, b = 3, c = a * b; a < 10; a++) { ... }
このようなトリックは多くの JavaScript フレームワークで使用されています。だからこそ、私たちはそれらについて言及しているのです。ただし、通常、コードの可読性は向上しないため、使用する前によく考える必要があります。
重要度: 5
以下のコードの後のすべての変数a
、 b
、 c
およびd
の最終値は何ですか?
a = 1、b = 1 とします。 c = ++a とします。 //? d = b++ とします。 //?
答えは次のとおりです。
a = 2
b = 2
c = 2
d = 1
a = 1、b = 1 とします。 アラート( ++a ); // 2、プレフィックス形式は新しい値を返します アラート( b++ ); // 1、後置形式は古い値を返します アラート( a ); // 2、1回インクリメント アラート( b ); // 2、1回インクリメント
重要性: 3
以下のコードの後のa
とx
の値は何ですか?
a = 2 とします。 x = 1 + (a *= 2) とします。
答えは次のとおりです。
a = 4
(2 倍)
x = 5
(1 + 4 として計算)
重要度: 5
これらの式の結果はどうなるでしょうか?
「」+1+0 "" - 1 + 0 真+偽 6/「3」 「2」*「3」 4 + 5 + "ピクセル" 「$」+4+5 「4」-2 「4ピクセル」 - 2 「 -9 」 + 5 「 -9 」 - 5 ヌル + 1 未定義 + 1 " t n" - 2
よく考えて書き出して、答えと比べてみましょう。
"" + 1 + 0 = "10" // (1) "" - 1 + 0 = -1 // (2) true + false = 1 6 / "3" = 2 「2」*「3」= 6 4 + 5 + "ピクセル" = "9ピクセル" "$" + 4 + 5 = "$45" 「4」-2 = 2 "4px" - 2 = NaN " -9 " + 5 = " -9 5" // (3) " -9 " - 5 = -14 // (4) null + 1 = 1 // (5) 未定義 + 1 = NaN // (6) " t n" - 2 = -2 // (7)
文字列"" + 1
を使用した加算は、 1
文字列"" + 1 = "1"
に変換します。すると、 "1" + 0
が得られ、同じルールが適用されます。
減算-
(ほとんどの数学演算と同様) は数値に対してのみ機能し、空の文字列""
を0
に変換します。
文字列を追加すると、文字列に数値5
が追加されます。
減算は常に数値に変換されるため、 " -9 "
は数値-9
になります (周囲のスペースは無視されます)。
数値変換後、 null
0
になります。
undefined
数値変換後にNaN
になります。
文字列が数値に変換されるときに、文字列の先頭と末尾からスペース文字が切り取られます。ここで、文字列全体は、 t
、 n
などのスペース文字とそれらの間の「通常の」スペースで構成されます。したがって、空の文字列と同様に、 0
になります。
重要度: 5
ユーザーに 2 つの数値を要求し、その合計を表示するコードを次に示します。
正しく動作しません。以下の例の出力は12
(デフォルトのプロンプト値の場合) です。
なぜ?修正してください。結果は3
になるはずです。
let a = プロンプト("最初の番号?", 1); let b = プロンプト("2 番目の番号?", 2); アラート(a + b); // 12
その理由は、プロンプトがユーザー入力を文字列として返すためです。
したがって、変数の値はそれぞれ"1"
と"2"
になります。
a = "1" とします。 // プロンプト("最初の番号?", 1); b = "2" とします。 // プロンプト("2 番目の番号?", 2); アラート(a + b); // 12
私たちがすべきことは、 +
の前に文字列を数値に変換することです。たとえば、 Number()
使用するか、先頭に+
を追加します。
たとえば、 prompt
の直前に次のようにします。
let a = +prompt("最初の番号?", 1); let b = +prompt("2 番目の番号?", 2); アラート(a + b); // 3
またはalert
内で:
let a = プロンプト("最初の番号?", 1); let b = プロンプト("2 番目の番号?", 2); アラート(+a + +b); // 3
最新のコードでは単項とバイナリ+
両方を使用しています。面白いですね。