LoginSignup
0
0

3.JavaScript演算子

Last updated at Posted at 2024-06-16

算術演算子

警告: 記事は機械翻訳があるため、学習メモとして保存、学習参考用のみ

演算子は既存の値から新しい値へのデータ処理の基本的な方法 JavaScriptには様々な演算子があり、主要な演算をすべてカバーしています。

概要

JavaScriptには10個の演算子があり、基本的な演算を行うことができます。

  • 加算演算子:x + y
  • 引き算演算子:x-y
  • 乗算子:x * y
  • 割り算演算子:x / y
  • 指数演算子:x ** y
  • 余り演算子:x % y
  • 自己増加演算子:++xあるいはx++
  • 自己減算子:——xあるいはx——
    数値演算子**:+x
  • マイナス値演算子:-x

引き算、掛け算、割り算の演算法は比較的単純で、それぞれの数学の演算を実行すること 。他にもいくつか算術演算子を紹介しますが、ポイントは加算演算子 。

加算子

基本ルール

足し算(足し算、+)は、2つの値の和を求める最も一般的な演算子 。

1 + 1 // 2 

JavaScriptでは数値以外の加算が可能 。

true + true // 2 
1 + true // 2 

上のコードでは1行目が2つのブール値の加算で2行目が数値とブール値の加算 どちらの場合も、ブール値は自動的に数値に変換され、加算されます。

特殊なのは、2つの文字列を加算した場合、加算演算子は連結演算子となり、新しい文字列を返して2つの元の文字列を連結します。

'a' + 'bc' // "abc" 

一方の演算子が文字列で、もう一方の演算子が非文字列の場合、非文字列は文字列に変換され、再び結合されます。

1 + 'a' // "1a" 
false + 'a' // "falsea" 

加算を実行するか、接続を実行するかは実行時に決定されます。つまり、演算子の違いによって文法が異なることを「オーバーロード(overload)」と呼びます。加算子にはオーバーロードがあり、両方の演算を実行する可能性があるので、注意が必要 。

'3' + 4 + 5 // "345" 
3 + 4 + '5' // "75" 

上記のコードでは、文字列の位置によって左から右への演算順序が異なります。

加算演算子以外の算術演算子(減算、割り算、乗算など)はオーバーロードしません。すべての演算子を数値に変換し、それに応じた数学的な演算を行うというルール 。

1 - '2' // -1 
1 * '2' // 2 
1 / '2' // 0.5 

上のコードの減算、割り算、乗算は、文字列を自動的に数値に変換してから演算します。

オブジェクトの加算 。

演算子がオブジェクトの場合、元の型の値に変換してから加算しなければなりません。

var obj = {p: 1} ;
obj + 2 // "[object object]2" 

上記のコードで、オブジェクトobjを元の型に変換した値は[object object]で、これに2を加えたもの 。

オブジェクトは元の型の値に変換されます。

まず、自動的にオブジェクトを呼び出すvalueOfメソッド 。

var obj = {p: 1} ;
obj.valueof () // {p: 1} 

一般的に、オブジェクトのvalueOfメソッドは常にオブジェクト自身に戻り、そこで自動的にオブジェクトのtoStringメソッドを呼び出して文字列に変換します。

var obj = {p: 1} ;
obj.valueof ().toString() // "[object object]"

オブジェクトのtoStringメソッドはデフォルトで[object object]を返すので、先ほどの例のような結果になります。

このルールを知っていれば、自分で「valueOf」メソッドや「toString」メソッドを定義して、望む結果を得ることができます。

var obj ={ 
valueOf: function(){ 
return 1 ;
} 
} ;

obj + 2 // 3 

上のコードではobjオブジェクトを定義するvalueOfメソッドは1を返しますobj + 23になりますこの例では、valueOfメソッドは元の型の値を直接返すので、toStringメソッドは呼び出されません。

以下はカスタム「toString」メソッドの例 。

var obj ={ 
toString: function () {
return 'hello' ;
} 
} ;

obj + 2 // "hello2" 

上のコードでオブジェクトobjのtoStringメソッドは文字列hello`を返します。前述したように、1つの演算子が文字列であれば、加算演算子は接続演算子となり、接続された文字列を返します。

ここで、演算子が「Date」オブジェクトのインスタンスの場合、「toString」メソッドが優先的に実行される特別な例があります。

var obj = new Date() ;
obj.valueof = function () {return 1};
obj.toString = function () {return 'hello'};

obj + 2 // "hello2" 

上記のコードでは、オブジェクトobjDateオブジェクトのインスタンスであり、valueOfメソッドとtoStringメソッドをカスタマイズしており、結果としてtoStringメソッドが優先的に実行されます。

余り演算子

余り演算子(%)は、前の演算子が後の演算子で割り算された余りを返します。

12 % 5/2 

なお、演算結果の符号は1つ目の演算子の符号によって決定されます。

- 1% // -1
1 % -2 // 1 

したがって、負の数の正確な余り値を得るためには、まず絶対値関数を使うことができます。

//間違った書き方 
function isOdd(n) {
  return n % 2 === 1;
}
isOdd(-5) // false
isOdd(-4) // false

// ただし
function isOdd(n) {
  return Math.abs(n % 2) === 1;
}
isOdd(-5) // true
isOdd(-4) // false

剰余演算子は浮動小数点演算にも用いることができます。しかし、浮動小数点数は正確な値ではないため、完全な正確さは得られません。

6.5 % 2.1
// 0.19999999999999973

自己増加と自己減算の演算子

自己増加と自己減算の演算子は単項演算子で演算子だけが必要 演算子をまず数値に変換し、1を足したり引いたりするの 。元の変数を修正します

var x = 1;
++x // 2
x // 2

--x // 1
x // 1

上のコードの変数xは増加した後、2を返して、更に減少して、1を返します。どちらの場合も元の変数xの値が変わります。

演算後、変数の値が変化する効果を演算の副作用(side effect)といいます。自己増加演算子と自己減算演算子は副作用のある演算子のうち2つだけで他の演算子は変数の値を変えません

自己増加と自己減算の1つの注意点は、変数の後に置くと、変数操作前の値を戻して自己増加/自己減算を行うこと 。変数の前に置いて、まずインクリメント/インクリメントの操作をしてから、変数操作後の値に戻ります。

var x = 1 ;
var y = 1 ;

x++ // 1 
++y // 2 

上のコードの中で、xは先に現在の値を返して、それから自己増加しますので、1を得ます;yは増加して新しい値を返すので2が得られます

数値演算子,負の数値演算子

数値演算子(+)も加算を使いますが、単項演算子(オペランドは1つ)で、加算演算子は2項演算子(オペランドは2つ) 。

数値演算子は、任意の値を数値に変換できるように働きます(Number関数と同じように働きます)。

+true // 1 
+[] // 0 。
+{} // NaN 

上のコードは、非数値が数値演算子を経て数値になっていることを示しています(最後の行NaNも数値)。具体的な型変換のルールについては、「データ型変換」の章を参照されたいと思います。

負の値演算子(-)は、同じように1つの値を数値に変換する機能を持っていますが、正負の値が得られます。2つの負の値の演算子を連用して、数値の演算子に等しい 。

var x = 1 ;
-x // -1 
-(-x) // 1 

上のコードの最後の行の括弧が必要 そうでないと自己減算子になってしまいます

数値記号と負の数値記号は元の変数の値を変えることなく新しい値を返します

指数演算子

指数演算子(**)は指数演算を行います。前の演算子が底で、次の演算子が指数 。

2 ** 4 // 16 

指数演算子は左結合ではなく右結合 つまり、複数の指数演算子を連用する場合は、一番右端の計算を先に行います。

//2 **(3 ** 2)に相当
2 ** 3 ** 2
// 512 

上記のコードでは、指数演算子は右結合なので、1つ目ではなく2つ目の指数演算子を先に計算します。

代入演算子

代入演算子(Assignment Operators)は、変数に値を与えます。

最も一般的な代値演算子は、もちろん等号(=) 。

//1を変数xに割り当てます。
var x = 1 ;

//変数yの値を変数xに割り当てます。
var x = y ;

代入演算子は他の演算子と組み合わせてバリエーションを作ることもできます。以下は算術演算子との組み合わせ 。

//はx = x + yに等しい 。
x += y 

//はx = x-yに等しい 。
x = y 

//はx = x * yに等しい 
x *= y 

//はx = x / yに等しい 。
x /= y 

// x = x % yに相当します。
x %= y 

// x = x ** yに等しい 。
x **= y 

以下はビット演算子との組み合わせ (ビット演算子については後述)。

// x = x >> yに等しい 。
x >>= y 

// x = x << yに相当します。
x <<= y 

// x = x >>> yに等しい 。
x >>>= y 

// x = x & yに等しい 。
x &= y 

/ /イコールx = x | y
x | = y

// x = x ^ yに等しい 。
x ^= y 

これらの複合的な付値演算子は、まず指定された演算を行い、左の変数に値を返します。

比較演算子

概要

比較演算子は2つの値の大きさを比較し、指定された条件が満たされているかどうかを示すブール値を返します。

2 > 1 // true 

上のコードは21より大きいかどうかを比較して、trueを返します。

なお、比較演算子は数値だけでなく様々な種類の値を比較することができます。

JavaScriptには8つの比較演算子があります。

  • >演算子より大きい
  • <は演算子より小さい 。
  • <=は演算子以下 。
  • >=演算子以上
  • ==等価演算子
  • ===厳密に等しい演算子 。
    ー !=不等価演算子 ー !==厳密な不等価演算子 。

8つの比較演算子は2種類に分けられます等価比較とそうでない比較 規則は違います。非等価比較の場合、アルゴリズムは2つの演算子が両方とも文字列かどうかを見て、そうであれば辞書順に比較します(実際にはUnicodeの符号点を比較します)。そうでなければ、両方の演算子を数値に変換し、その数値の大きさを比較します。

非等価演算子:文字列の比較

文字列は辞書順に比較されます。

'cat' > 'dog' // false 
'cat' > 'catalog' // false 

JavaScriptエンジン内部では、最初に頭文字のUnicodeコードポイントを比較します。等しい場合は2番目の文字のUnicode符号点を比較します。

'cat' > 'cat' // true' 

上のコードでは、小文字のcのUnicode点(99)が大文字のcのUnicode点(67)より大きいため、trueが返されます。

すべての文字にUnicodeの符号点があるため、漢字の比較も可能 。

'' > '' // false

上のコードでは、"大"のUnicodeの符号点は22823で、"小"は23567なのでfalseを返します。

非等価演算子:非文字列の比較

2つの演算子のうち少なくとも1つが文字列でない場合は、次の2つに分ける必要があります。

(1)元の型値

両方の演算子が元の型の値の場合は、数値に変換してから比較します。

5 > '4' // true 
// 5 > Number('4')に相当します。
//つまり5 > 4 

true > false // true 
//はNumber(true) > Number(false)に相当します。
//つまり1 > 0 

2 > true // true 
// 2 > Number(true)に相当します。
//つまり2 > 1 

上のコードでは、文字列とブール値はいずれも数値に変換してから比較します。

ここで注意が必要なのはNaNとの比較 。' NaN '自身を含む任意の値と' NaN 'が等しくない演算子を使って比較すると' false 'が返ってきます。

1 > NaN // false 
1 <= NaN // false 
'1' > NaN // false 
'1' <= NaN // false
< NaN > NaN // false 
<= NaN // false >

(2)対象

演算子が対象の場合は、元の型の値に変換して比較します。

オブジェクトは元の型の値に変換され、アルゴリズムはまずvalueOfメソッドを呼び出します。オブジェクトが戻ってきた場合は、「toString」メソッドを呼び出します。詳しくは「データタイプの変換」の章を参照してください。

var x =[2] ;
x > '11' // true 
//[2].valueOf().toString() > '11'に相当します。
//即'2' > '11' 

x. valueof = function () {return '1'};
x > '11' // false 
//は(function () {return '1'})() > '11'に相当します。
//即'1' > '11' 

2つの対象の比較も同じ 。

[2] > [1] // true 。
//どもっ[2]. valueof () . tostring () > [1] . valueof () . tostring ()
//即'2' > '1' 

[2] > [11] // true 。
//どもっ[2]. valueof () . tostring () > [11] . valueof () . tostring ()
//即'2' > '11' 

({x: 2}) >= ({x: 1}) // true 
//どもった(2}){x: . valueof () . tostring () > = ({x: 1}) . valueof () . tostring ()
//即'[object object]' >= '[object object]' 

厳密な等価演算子

JavaScriptには2種類の等価演算子があります:=====

簡単に言えば、等価演算子(==)は2つの値が等しいかどうかを比較し、厳密な等価演算子(===)は「同じ値」かどうかを比較します。2つの値が同じ型でない場合、厳密な等価演算子(===)は直接falseを返し、等価演算子(==)はそれらを同じ型に変換し、厳密な等価演算子で比較します。

ここでは厳密な等価演算子のアルゴリズムを紹介します。

**(1)異なる種類の値 **

2つの値の型が異なる場合は、直接falseを返します。

1 === "1" // false 
"true" // false 

上記のコードでは、数値の1と文字列の" 1 "、ブール値のtrueと文字列の"true"を比較していますが、型が異なるため、同じfalse`となります。

(2)同じクラスの元の型値

同じ型の元の型の値(数値、文字列、ブール値)を比較した場合、同じ値ならtrue、違う値ならfalseを返します。

1 === 0x1 // true 

上のコードは十進法の1と十六進法の1を比較します。型も値も同じなのでtrueを返します。

NaNは(それ自身を含めて)どの値とも等しくありません。また、正0は負0

NaN === NaN // false
+0 === -0 // true 

(3)複合型値

2つの複合型(オブジェクト、配列、関数)のデータを比較するとき、それらの値が等しいかどうかではなく、同じアドレスを指しているかどうかを比較します。

{} === {} // false 
[] === [] // false 
(function () {} === function () {}) // false

上のコードはそれぞれ2つのヌルオブジェクト、2つのヌル配列、2つのヌル関数を比較して、結果はすべて等しくありません。なぜなら、復合型の値については、厳密に等しい演算は同じメモリアドレスを参照しているかどうかを比較し、演算子の両方の空のオブジェクト、空の配列、空の関数の値は、すべて異なるメモリアドレスに格納され、結果は当然false

2つの変数が同じオブジェクトを参照している場合、それらは等しい 。

var v1 ={} ;
var v2 = v1 ;
v1 === v2 // true 。

なお、2つのオブジェクトの比較では、厳密に等しい演算子はアドレスを、それ以上の演算子は値を比較します。

var obj1 ={} ;
var obj2 ={} ;

obj1 > obj2 // false 
obj1 < obj2 // false > 。
obj1 === obj2 // false 

上の3つの比較のうち、最初の2つは値、最後の1つはアドレスを比較していますので、いずれもfalseを返します。

** (4) undefinedとnull**

' undefined 'と' null 'は自分自身と厳密に等しい 。

undefined === undefined // true
null == null // true 

宣言された変数のデフォルト値は' undefined 'なので、宣言されていない2つの変数は等しいことになります。

var v1 ;
var v2 ;
v1 === v2 // true 。

厳密な不等価演算子

厳密な等価演算子には「厳密な不等価演算子」(!==)、そのアルゴリズムは厳密な等価演算子の結果を求めてから、逆の値を返します。

1 !== '1' // true 
//に等しい 
 !(1 === '1' )

上のコードは感嘆符 !は後の式の逆の値を求めるもの 。

等価演算子

等価演算子は同じ種類のデータを比較するのに使われ、厳密な等価演算子と全く同じ 。

1 == 1.0 
//に等しい 
1 === 1.0 

異なる種類のデータを比較するとき、等価演算子はデータを型変換してから厳密な等価演算子で比較します。以下、いくつかのケースに分けて、異なる種類の値を比較するためのルールを検討します。

(1)元の型値

元の型の値は数値に変換して比較します。

1 == true // true 
//1 === Number(true)に相当します。

0 == false // true 
//0 === Number(false)に相当します。

2 == true // false 
//2 === Number(true)に相当します。

2 == false // false 
//2 === Number(false)に相当します。

'true' == true // false 
//Number('true') === Number(true)に相当します。
//NaN === 1に相当します。

'' == 0 // true 
//はNumber('') === 0に等しい 。
0 === 0に等しい 

'' == false // true 
//Number('') === Number(false)に相当します。
//0 === 0に等しい 

'1' == true // true 
//はNumber('1') === Number(true)と同じ 。
//1 === 1に等しい 

'\n 123 \t' == 123 // true 
//というのは、文字列が数字になるときに、前・後のフレームが省略されるから 。

上のコードは文字列とブール値の両方を数値化して比較します具体的な文字列やブール値の型変換規則については、「データ型変換」の章を参照されたいと思います。

(2)オブジェクトを元の型値と比較します

オブジェクト(ここでは配列や関数を含む広義のオブジェクトを指す)が元の型の値と比較された場合、オブジェクトは元の型の値に変換され、さらに比較されます。

具体的には、先にオブジェクトのvalueOf()メソッドを呼び出して、元の型の値を得たら、前のセクションの規則に従って、お互いを比較します。それでもオブジェクトが出てきたら、再びtoString()メソッドを呼び出して文字列形式を得て、比較します。

元の型値と配列を比較した例を以下に示します。

//配列と数値の比較 。
[1] == 1 // true 

//配列と文字列の比較 
[1] == '1' // true 
[1, 2] == '1,2' // true 

//オブジェクトとブール値の比較 
[1] == true 
[2] == true // false 。

先の例では、JavaScriptエンジンは先に配列[1]に対して配列のvalueOf()メソッドを呼び出し、返ってくるのはやはり配列なので、次に配列のtoString()メソッドを呼び出し、文字列形式を得て、前のセクションのルールに従って比較します。

以下はもっと直接的な例 。

const obj ={ 
valueOf: function(){ 
console.log('実行valueOf()');
return obj ;
} ,
toString: function () {
console.log('実行toString()');
return 'foo' ;
} 
} ;

obj == 'foo' 
//実行valueOf() 。
// 実行toString()
// true 

上記の例では、objvalueOf()toString()のメソッドをカスタマイズしたオブジェクト 。このオブジェクトを文字列'foo'と比較すると、valueOf()toString()メソッドが次々に呼び出され、最後に'foo'が返されるので、比較結果はtrueとなります。

** (3) undefinedとnull**

「undefined」と「null」は、自分自身と比較したり、お互いを比較したりした場合にのみ、「true」を返します。他の種類の値と比較すると「false」となります。

undefined = undefined // true 
null == null // true 
undefined == null // true 

false == null // false 
false == undefined 

0 == null // false 
0 == undefined // false 

(4)等価演算子の欠点

等価演算子の暗黙の型変換は、直観に反する結果をもたらします。

0 == '' // true 
0 == '0' // true 

2 == true // false 
2 == false // false 

false == 'false' // false 
false == '0' // true 

false == undefined 
false == null // false 
null == undefined // true 

' \t\r\n ' == 0 // true 

これらの式は直感とは異なり、間違いやすいもの 。したがって、等価演算子(==)を使用せず、厳密な等価演算子(===)のみを使用することをお勧めします。

不等価演算子

等価演算子には対応する「不等価演算子」(!=)で、そのアルゴリズムはまず等しい演算子の結果を求めて、それから反対の値を返します。

1 != '1' // false 

//に等しい 
 !(1 == '1' )

ブール演算子

概要

ブール演算子は式をブール値に変換するのに使われ、4つの演算子からなります。

-は逆演算子を取ります:!
-かつ演算子:&&
-や演算:| |
-三元演算子:` か?「 」

逆演算子を取ります(!)

逆演算子はブールの値を逆にするための感嘆符 つまりtruefalseに、falsetrueになります

 !true // false 
 !false // true 

非ブール値の場合、逆演算子を取るとブール値に変換されます。次の六つの値を逆算して「true」、それ以外の値は「false」と覚えておくといいでしょう。

  • ' undefined '
  • `null
  • `false
  • 0
  • NaN 空文字列(''`)
 !undefined // true
!null // true
!0 // true
!NaN // true
!"" // true

!54 // false
!'hello' // false
![] // false
!{} // false

上のコードでは、どのような種類の値であっても、取逆演算をするとブール値になります。

1つの値を2回連続して逆演算すると、対応するブール値に変換することになり、ブーラン関数と同じ働きをします。これはタイプ変換でよく使われる書き方 。

!!x
// 等
Boolean(x)

上のコードでは、xがどのような種類の値であっても、2回の逆演算を経て、Boolean関数の結果と同じブール値になります。したがって、2回逆にするというのは、ある値をブール値に変換するという簡単な書き方 。

かつ演算子(&&) 。

演算子(&&)は多くの式の値を求めます。

1つ目の演算子のブール値が' true 'なら2つ目の演算子の値を返します(ブール値ではなく値 ので注意)。1つ目の演算子のブール値がfalseであれば、1つ目の演算子の値を返し、2つ目の演算子の値は求めません。

't' && '' // ""
't' && 'f' // "f"
't' && (1 + 2) // 3
'' && 'f' // ""
'' && '' // ""

var x = 1;
(1 - 1) && ( x += 1) // 0
x // 1

上記のコードの最後の例では、1つ目の演算子のブール値がfalseである場合は、2つ目の演算子の値を求めずにその値0を返すので、変数xの値は変わりません。

この2つ目の演算子を飛ばしてしまう仕組みを「ショート」と呼びます。プログラマの中にはif構造の代わりにそれを使う人もいます。例えば、次のようなif構造のコードを使って書き換えることができます。

if (i) {
  doSomething();
}

// 等

i && doSomething();

上記の2つの表記は等価 が、後者は目的が見えにくく、除却もしにくいので、慎重に使うことをお勧めします。

この場合、1つ目のブール値がfalseである式の値を返します。すべての式のブール値がtrueの場合は、最後の式の値を返します。

true && 'foo' && '' && 4 && 'foo' && true
// ''

1 && 2 && 3
// 3

例1では最初のブール値がfalseの式は3番目の式なので空の文字列になります例2では全ての式のブール値はtrueなので最後の式の値3を返します

或演算(|)|

或演算(| |)も余りエクスプレション用の求値。1つ目の演算子のブール値がtrueの場合、1つ目の演算子の値を返し、2つ目の演算子の値を求めません。1つ目の演算子のブール値がfalseであれば、2つ目の演算子の値を返します。

't' || '' // "t"
't' || 'f' // "t"
'' || 'f' // "f"
'' || '' // ""

短絡規則はこの演算子にも適用されます。

var x = 1;
true || (x = 2) // true
x // 1

上のコードでは、1つ目の演算子はtrueなので、2つ目の演算子はもう実行しません。よって、xの値は変わりません。このように、1つ目の式の値だけで2つ目の式を実行するかどうかを制御する仕組みを「ショート」と呼びます。

または演算子は複数連用できます。この場合、最初のブール値がtrueの式の値を返します。全ての式がfalseの場合は最後の式の値を返します

false || 0 || '' || 4 || 'foo' || true
// 4

false || 0 || ''
// ''

上記のコードの例では、最初のブール値がtrueの式は4番目の式なので、数値4が得られます。例2では、すべての式のブール値はfalseなので、最後の式の値を返します。

変数のデフォルト値を設定するときによく使われます

function saveText(text) {
  text = text || '';
  // ...
}

// 或
saveText(this.text || '')

上記のコードは、引数が関数呼び出し時に提供されていない場合、引数がデフォルトで空文字列に設定されていることを示しています。

三項条件演算子?:

三項条件演算子は疑問符(?) 。コロン(:)で3つの式を区切りますJavaScript言語で3つの演算子を必要とする唯一の演算子 。1つ目の式のブール値がtrueの場合は2つ目の式を返しますそうでない場合は3つ目の式を返します

't' ? 'hello' : 'world' // "hello"
0 ? 'hello' : 'world' // "world"

上のコードのt0のブール値はそれぞれtruefalseなので、それぞれ2番目と3番目の式の値を返します。

通常、三項条件式はif…else文にも同様の効果があり、前者でも表現できるものは後者でも表現できます。しかし両者には大きな違いがありますif…elseは文であり、値を返しません。三項条件式は、戻り値を持つ式 。よって、値を返す必要がある場合は、3つの条件式しか使えず、if..は使えません。else

console.log(true ? 'T' : 'F');

上記のコードでは、console.logメソッドのパラメータは1つの式でなければなりません。この場合、3つの条件式しか使えません。もし使いますif…else文はコードの書き方を変えなければなりません

バイナリビット演算子

概要

バイナリビット演算子は、バイナリビットを直接計算するために使用され、全部で7つあります。

  • バイナリまたは演算子 (or): 記号は '|' で、両方のバイナリ ビットが '0' の場合、結果は '0' になり、それ以外の場合は '1' になります。
  • 二項 & 演算子 (and): 記号 '&' は、両方の 2 進ビットが 1 の場合、結果は 1 になり、それ以外の場合は 0 であることを意味します。
  • 二項演算子なし(not):記号は「~」で、2進ビットの否定を示します。
  • XOR演算子:記号「^」は、2つのバイナリビットが同じでない場合、結果は1になり、そうでない場合は0になることを意味します。
  • 左シフト:以下で説明するように、記号は「<<」 。 - 右シフト:以下で説明するように、記号は「>>」 。
  • ゼロで塗りつぶされた右シフト:以下で説明するように、記号は「>>>」 。

これらのビット演算子は各ビットを直接処理するため、非常に低レベルの操作であり、利点は非常に高速であり、欠点は非常に直感的でないことであり、多くの場合、コードの理解とトラブルシューティングが困難になります。

ビット演算子は整数でのみ機能し、演算子が整数でない場合は、実行前に自動的に整数に変換されることに注意することが重要 。 また、JavaScript では値は 64 ビット浮動小数点数として格納されますが、ビット演算は 32 ビット符号付き整数として実行され、戻り値も 32 ビット符号付き整数 。

i = i | 0;

上記のコード行の意味は、「i」(整数または10進数)を32ビット整数に変換すること 。

この機能を使用すると、任意の値を 32 ビット整数に変換する関数を記述できます。

function toInt32(x) {
  return x | 0;
}

上記の関数は「0」または算術値を持つ任意の値を取り、このビット演算は値を32ビット整数に自動的に変換します。 この機能の使用方法は次のとおり 。

toInt32(1.001) // 1
toInt32(1.999) // 1
toInt32(1) // 1
toInt32(-1) // -1
toInt32(Math.pow(2, 32) + 1) // 1
toInt32(Math.pow(2, 32) - 1) // -1

上記のコードでは、「toInt32」は小数を整数に変換できます。 通常の整数の場合、戻り値に変更はありません。 2 の 32 乗以上の整数の場合、32 桁を超える桁は丸められます。

二項演算子

二項演算子 ('|') は 2 つの演算子をビット単位で比較し、2 つの 2 進数ビットのうち 1 つだけが '1' であれば '1' を返し、そうでなければ '0' を返します。

0 | 3 // 3

上記のコードでは、「0」と「3」のバイナリ形式はそれぞれ「00」と「11」であるため、バイナリまたは算術演算を行うと「11」(つまり「3」)になります。

ビット演算は整数に対してのみ有効であり、小数点が検出されると、小数部分が四捨五入され、整数部分のみが保持されます。 したがって、2進数または算術の小数点が「0」の場合、数値の小数部分を削除すること、つまり桁全体を取ることと同等 。

2.9 | 0 // 2
-2.9 | 0 // -2

この丸め方法は、32ビット整数「2147483647」の最大値を超える数値には適用されないことに注意してください。

2147483649.4 | 0;
// -2147483647

二項演算子 &

二項演算子 ('&') のルールは、2 つの演算子をビットごとに比較することであり、2 つのバイナリ ビットの 1 つが '0' である限り、'0' を返し、それ以外の場合は '1' を返します。

0 & 3 // 0

上記のコードでは、0 (バイナリ '00') と 3 (バイナリ '11') のバイナリと算術演算の結果は '00' (つまり '0') になります。

二項 no 演算子

二項 no 演算子 ('~') は、各 2 進数ビットを逆の値に変更します ('0' は '1' になり、'1' は '0' になります)。 結果は、コンピュータ内部の数値表現メカニズムのために理解しにくい場合があります。

~ 3 // -4

上記の式は、'3'に対してバイナリの負の演算を行い、'-4'を与えます。 これは、JavaScript が内部 べての演算子を 32 ビットの 2 進整数に変換してから操作を実行するため 。

32 ビット整数形式の '3' は '00000000000000000000000000000011' で、2 進数の負の演算の結果は '11111111111111111111111111111100' になります。 最初のビット (符号ビット) は 1 であるため、この数値は負の数 。 JavaScript は内部的に補数の形式を使用して負の数を表します、つまり、この数値から 1 を減算し、もう一度逆を取り、負の符号を追加して負の数に対応する 10 進値を取得する必要があります。 この数値から 1 を引くと「11111111111111111111111111111011」になり、もう一度取ると「00000000000000000000000000000100」になり、さらにマイナス記号は「-4」になります。 このようなプロセスがより面倒であることを考えると、-1に等しい逆値に数値が加算されることを単純に覚えておくことができます。

~ -3 // 2

上記の式は次のように計算でき、「-3」の逆数は「-1」から「-3」を引いた値に等しく、結果は「2」になります。

整数に対して 2 回連続で 2 回連続して 2 項の演算を行い、それ自体を取得します。

~~3 // 3

すべてのビット演算は、整数に対してのみ有効 。 2 進数の負の演算で 10 進数が検出されると、小数部分も四捨五入され、整数部分のみが保持されます。 したがって、10 進数に対する 2 つの連続した 2 進数の no 演算は、丸めの効果を得ることができます。

~~2.9 // 2
~~47.11 // 47
~~1.9999 // 1
~~3 // 3

算術演算を使用しない 2 進数を使用した丸めは、すべての丸め方法の中で最も高速 。

文字列に対してバイナリ no 操作を実行するために、JavaScript エンジンは最初に 'Number' 関数を呼び出して文字列を数値に変換します。

~// 相当 ~Number('011')
~'011'  // -12

// 相当~Number('42 cats')
~'42 cats' // -1

// 相当~Number('0xcafebabe')
~'0xcafebabe' // 889275713

// 相当~Number('deadbeef')
~'deadbeef' // -1

「Number」関数で文字列を数値に変換するためのルールについては、「データの型変換」の章を参照してください。

他のタイプの値の場合、二項の no 演算も最初に 'Number' の数値に変換されてから処理されます。

~Number([]) と等価
~[] // -1

~Number(NaN) と同等
~NaN // -1

~Number(null) と同等
~null // -1

XOR演算子

XOR 演算 ('^') は、2 つの 2 進ビットが同じでない場合は '1' を返し、2 つの 2 進ビットが同じ場合は '0' を返します。

0 ^ 3 // 3

上記の式では、'0'(バイナリ'00')と'3'(バイナリ'11')はXORであり、それぞれのバイナリビットが異なるため、'11'(つまり'3')が得られます。

「XOR演算」には特別なアプリケーションがあり、2つの数値「a」と「b」を連続して3つのXOR演算「a^=b; b^=a; a^=b; 'は、それらの値を交換できます。 つまり、XOR演算を使用して、一時変数を導入せずに2つの変数の値を入れ替えることができます。

var a = 10;
var b = 99;

a ^= b, b ^= a, a ^= b;

a // 99
b // 10

これは、2 つの変数の値を入れ替える最速の方法 。

XOR 演算は、切り上げにも使用できます。

12.9 ^ 0 // 12

Shift-Left 演算子

左シフト演算子 ('<<') は、数値のバイナリ値を '0' で終わる指定された桁数だけ左にシフトする、つまり '2' を指定された累乗に乗算することを意味します。 左に移動すると、最上位の符号ビットが一緒に移動します。

//4 の 2 進数は 100  。
//左に 1 つシフトすると 1000 (つまり、10 進数で 8) になります。
//2 の 1 乗に相当
4 << 1
// 8

-4 << 1
// -8

上記のコードでは、'-4' の 2 進数形式が '11111111111111111111111111111100' であるため、'-4' を 1 つ左にシフトして '-8' を取得し、数値を左にシフトして '11111111111111111111111111111000' を取得し、10 進数に変換され (1 で減算して否定し、負の符号を加えて) '-8' になります。

左に 0 ビットシフトすると、値を 32 ビット整数に変換することと同等であり、丸めと同等であり、正の数と負の数の両方に有効 。

13.5 << 0
// 13

-13.5 << 0
// -13

シフトレフト演算子は、バイナリ値に非常に便利 。

var color = {r: 186, g: 218, b: 85};

// RGB to HEX
// (1 << 24)の作用は 结果が6桁数を保証する
var rgb2hex = function(r, g, b) {
  return '#' + ((1 << 24) + (r << 16) + (g << 8) + b)
    .toString(16) // 16進制に変更、そしてstringに変更
    .substr(1);   // 
}

rgb2hex(color.r, color.g, color.b)
// "#bada55"

上記のコードでは、シフトレフト演算子を使用して、色のRGB値をHEX値に変換しています。

右シフト演算子

右シフト演算子 ('>>') は、数値の 2 進値を右にシフトする桁数を示します。 正の数の場合、すべての表は「0」で埋められます。 ネガティブな場合は、頭に「1」を作ります。 右シフト演算子は、基本的には、指定された累乗の '2' で除算することと同等 (最上位ビットである符号ビットが移動に関与します)。

4 >> 1
// 2
/*
4 の 2 進数形式は 00000000000000000000000000000100 なので、
右に1つ移動すると00000000000000000000000000000010、
これは小数点以下の 2  
*/

-4 >> 1
// -2
/*
-4 の 2 進数形式は 11111111111111111111111111111100 であるため、
右に1つ移動し、頭を1つにして、11111111111111111111111111111110を取得し、
これは 10 進数で -2  
*/

右シフト操作は、2 の分割可能性をシミュレートします。

5 >> 1
// 2
//5 / 2 = 2 に相当します。

2>>21
// 5
//21 / 4 = 5 に相当します。

3>>21
// 2
//21 / 8 = 2 に相当します。

4>>21
// 1
//21 / 16 = 1 に相当します。

ヘッドゼロの右シフト演算子

右シフト演算子 ('>>>') と右シフト演算子 ('>>') の違いは 1 つだけで、数値の 2 進形式を右にシフトすると、符号ビットに関係なくヘッドがゼロになること 。 したがって、操作は常に正の値を取得します。 正の数の場合、この操作の結果は右シフト演算子 ('>>') の結果とまったく同じ が、主な違いは負の数にあります。

4 >>> 1
// 2

-4 >>> 1
// 2147483646
/*
-4 の 2 進数形式は 11111111111111111111111111111100 であるため、
符号付きビットを 1 ビット右にシフトして01111111111111111111111111111110を取得します。
これは小数点以下の2147483646 。
*/

この操作は、実際には値を 32 ビット符号なし整数に変換します。

負の整数がコンピューター内にどのように格納されているかを確認する最も速い方法は、この演算子を使用すること 。

-1 >>> 0 // 4294967295

上記のコードは、'-1' が 32 ビット整数として使用される場合、内部ストレージ形式は符号なし整数形式で解釈され、値が 4294967295 (つまり、'(2^32)-1'、つまり '11111111111111111111111111111111' に等しい) であることを示しています。

スイッチのスイッチ

ビット演算子はオブジェクトの属性を設定するスイッチとして使うことができます。

あるオブジェクトに4つのスイッチがあり、それぞれが1つの変数だとします。では、4桁の2進数を設定し、それぞれのビットにスイッチを入れます。

var FLAG_A = 1 ;// 0001 
var FLAG_B = 2 ;// 0010 
var FLAG_C = 4 ;// 0100 
var FLAG_D = 8 ;// 1000 

上のコードはA、B、C、Dの4つのスイッチを設定し、それぞれがバイナリビットを占有しています。

すると、バイナリと演算を使って、現在の設定で指定のスイッチが入っているかどうかをチェックすることができます。

var flags = 5 ;//バイナリの0101 。

if (flags & FLAG_C){ 
//… 
} 
// 0101 & 0100 => 0100 => true 

上のコードはスイッチCがオンになったかどうかを検査します。開いていれば「true」、そうでなければ「false」と返されます。

ここで、「A」、「B」、「D」の3つのスイッチをオンにすると仮定して、マスク変数を作ることができます。

var mask = FLAG_A | FLAG_B | FLAG_D;
// 0001 | 0010 | 1000 => 1011

上のコードはABDの3つの変数に対して2進数あるいは演算を行って、マスク値が2進数の1011を得ます。

マスクのおかげで、バイナリや演算によって指定されたスイッチが確実にオンになります。

flags = flags | mask;

上のコードで計算された変数「flags」は3つのスイッチのバイナリビットがオンになっています

バイナリと演算は、スイッチの設定と異なるすべての現在の設定をオフにすることができます。

flags = flags & mask ;

排他的論理和演算は現在の設定を切り替えることができます。つまり、最初に実行すると現在の設定とは逆の値になり、もう一度実行すると元の値になります。

flags = flags ^ mask;

2進数の否定的な演算は現在の設定を反転することができて、すなわち元は0に設定して、演算後1になります;元は1に設定して、演算後に0になります。

flags = ~flags ;

##リンク参照

その他の演算子,演算順序

void演算子

' void '演算子は式を実行して何の値も返さずに' undefined 'を返します

void 0 // undefined 
void(0) // undefined 

上はvoid演算子の二種類の書き方で、どちらも正しい 。後者の、いつもカギ括弧を使うことをおすすめします。void演算子は優先度が高いので、括弧を使わないと間違った結果になりやすい 。例えば、void 4 + 7は、実際には(void 4) + 7と同じ 。

以下はvoid演算子の一例 。

var x = 3 ;
void (x = 5) //undefined 
x // 5 

この演算子の主な用途は、ブラウザのブックマークレットや、ハイパーリンクにコードを挿入してページのジャンプを防ぐこと 。

下記のコードをご覧いただきます。

<script>
function f() {
  console.log('Hello World');
}
</script>
<a href="http://example.com" onclick="f(); return false;"> click </a>

上記のコードでは、リンクをクリックすると「onclick」のコードが先に実行されます。onclickは「false」を返しますので、example.comに遷移することはありません。

void演算子は上記の代わりに書くことができます。

<a href="javascript: void(f())">文字</a>

以下はより現実的な例で、ユーザーがリンクをクリックしてフォームを提出しても、ページ遷移が発生しません。

<a href="javascript: void(document.form.submit())">
提出します
</a>

コンマ演算子

コンマ演算子は2つの式の値を求め、後者の式の値を返します。

'a', 'b' // "b" 

var x = 0 ;
var y = (x++, 10) ;
x // 1 
y // 10 

上のコードではコンマ演算子は次の式の値を返します

コンマ演算子の1つの用途は、ある値を返す前に補助的な操作を行うこと 。

var value = (console.log('Hi!'), true );
//ハイ !

value // true 

上のコードでは、コンマの前の動作を先に実行し、コンマの後の値を返します。

演算手順 。

優先度

JavaScriptでは、各種演算子の優先度(Operator Precedence)が異なります。優先度の高い演算子が先に実行され、優先度の低い演算子が後に実行されます。

4 + 5 * 6 // 34 

上のコードでは乗算子(*)の方が加算子(+)より優先度が高いので、乗算を先に実行して加算を実行すると次のようになります。

4 +(5 * 6) // 34 。

複数の演算子が混在していると、戸惑うようなコードになってしまうことがあります。

var x = 1;
var arr = [];

var y = arr.length <= 0 || arr[0] === undefined ? x : arr[0];

上のコードでは変数yの値がわかりにくいの が、この式には5つの演算子が含まれているので、誰が一番優先度が高いのか、なかなか覚えられません。

言語規格によって、この五演算の優先順位が高いから低い順:以下(< =)、厳格だ相(= = =)や(| |)、三元( ?:)、イコール(=) 。したがって、上の式では、実際の手順は次のようになります。

var y = ((arr.length <= 0) || (arr[0] === undefined)) ? x : arr[0];

すべての演算子の優先度を覚えるのは、とても難しく、必要もありません。

括弧の役割

括弧(())は最も優先度が高く、括弧内の式が1番目の演算を行うので、演算の優先度を上げることができます。

(4 + 5) * 6 // 54 。

上のコードでは、括弧を使っているので、加算が乗算より先に実行されます。

演算子の優先度は非常に複雑で厳格に規定されているため、常に括弧を使うことをお勧めします。

ちなみに括弧は演算子ではなく構文構造 2つの使い方があります。一つは式を括弧の中に入れて優先度を上げる方法。もう1つは関数の後に続き関数を呼び出すこと

なお、括弧は演算子ではありませんので、演算の優先度のみを変更する求値作用はありません。

var x = 1;
(x) = 2;

上のコードの2行目は、括弧に求値作用があれば1 = 2となりますが、これはエラー 。しかし、上のコードは実行でき、括弧は優先度を変更するだけで、値は求めません。

つまり、式全体が丸括弧の中に入っていては、何の効果もありません。

(expression )
//に等しい 
expression 

関数は括弧の中に入れられ、関数自身に戻ります。括弧が関数の後に続く場合は、関数を呼び出していることを意味します。

function f() {
  return 1;
}

(f) // function f(){return 1;}
f() // 1

上のコードでは、括弧の中に入れた関数は関数自身に戻り、括弧が続く関数は関数を呼び出します。

括弧の中には、式しか入れません。括弧の中に文章を入れると、エラーが発生します。

(var a = 1)
// SyntaxError: Unexpected token var

左結合と右結合

同じ優先度の演算子が同時に現れた場合、計算の順序の問題があります。

a OP b OP c

上のコードではOPは演算子を表しますこれには二つの解釈があります。

方式その1 
(a OP b) OP c 

//方式2 
a OP (b OP c) 

上記の2つの方法では、往々にして得られる計算結果が異なります。1つは、左の2つの演算子を結合して、このような解釈の演算子を採用して、「左結合」(left-to-right associativity)演算子と呼ばれます。2つ目は、右側の2つの演算子を結合したもので、これを「右結合」演算子(right-to-left associativity)と呼びます。

JavaScript言語のほとんどの演算子は「左結合」 。次の加算子の例を見てください。

x + y + z 

//エンジンは次のとおり 
(x + y) + z 

上のコードでは、xyが結合され、それらの計算結果はzと演算されます。

少数の演算子は「右結合」 が、主なものは賦値演算子(=)と三項条件演算子(?

w = x = y = z;
q = a ? b : c ? d : e ? f : g;

上記のコードの解釈は以下の通り 。

w = (x = (y = z));
q = a ? b : (c ? d : (e ? f : g));

上の2行は右側の演算を組み合わせたもの

また、指数演算子(**)も右結合 。

2 ** 3 ** 2 
//2 **(3 ** 2)に相当します。
// 512 
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0