JavaScriptの演算子について

こんにちは。
今回はまた[JavaScript本格入門](https://www.amazon.co.jp/dp/B01LYO6C1N/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)の内容のアウトプットをしようかと思います。
##等価演算子と同値演算子とは？
###等価演算子とは
等価演算子「==」は左辺と右辺の値を比較して、等しい場合にはtrue、等しくない場合にはfalseを返す演算子です。
しかし、**オペランドのデータ型によって比較の基準が違ってくる**ので注意しましょう。

[JavaScript本格入門](https://www.amazon.co.jp/dp/B01LYO6C1N/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)では等価演算子を**「オペランドのデータ型が異なっていてもデータ型を変換して『なんとか等しいと見なせないか』を試みる」**と説明しています。
例えば以下のような数値と論理値の比較も等価演算子を使用するとtrueと返ってきます。

```JavaScript
console.log(1 == true); //結果：true
```

また、等価演算子で特に注意が必要なのが比較の対象が配列/オブジェクト同士である場合です。
 
```JavaScript
let data1 = ['JavaScript', 'Ajax', 'ASP.NET'];
let data2 = ['JavaScript', 'Ajax', 'ASP.NET'];
console.log(data1 == data2); //結果：false
```
上記の例のように見かけでは同じ内容の変数であっても結果はfalseが返ってきます。
これは配列/オブジェクトのような参照型の場合には変数に**参照値**が格納されるためです。
そのため、中身は一緒であったとしても変数に格納されている**メモリ上のアドレスである参照値が異なる**のでfalseを返します。

###同値演算子とは
等価演算子はデータ型を変換して判定をしてくれるので、開発者側としてはデータ型をそこまで意識することなくコーディングができます。
しかし、その便利な機能が裏目に出て以下のような問題が発生することがあります。

```JavaScript
console.log('3.14E2' == 314);
console.log('0x10' == 16);
console.log('1' == 1);
//結果は全てtrue
```
3.14E2は指数表現として解釈、0x10は16進数として解釈がされてしまっています。
これでは思わぬ問題が発生しかねません。
そこで登場するのが**同値演算子「===」**です。
同値演算子はデータ型を変換しないという点を除いて等価演算子と同じ働きをします。

```JavaScript
console.log('3.14E2' === 314);
console.log('0x10' === 16);
console.log('1' === 1);
//結果は全てfalse
```
JavaScriptはデータ型に寛容にできている言語ではありますが、しっかりデータ型を区別しています。
大規模なアプリ開発ではこの寛容さがバグの原因となるそうなので、値を比較する際には同値演算子を使用するよう意識した方が良いでしょう。

##条件演算子とは
これは僕は知らなかった演算子なのですが、条件演算子というものがあるそうです。
条件演算子は**指定された条件式の真偽に応じて、対応する式の値を出力したい場合**に使用します。

```javascript
let x = 80;
console.log((x >= 70) ? '合格' : '不合格'); //結果：合格
```

if文を使用すれば同様の処理ができますが、**出力する値を条件に応じて振り分けたい**という状況であればこちらの方がシンプルにコードがかけます。

##論理演算子
論理演算子は**複数の条件式、論理値を論理的に結合して、その結果をtrueもしくはfalseで返します。**

・&&
左右の式が**ともに**trueの場合にtrueを返す。

・||
左右の式の**どちらか**がtrueの場合にtrueを返す。

・！
式がfalseの場合にtrueを返す。

```javascript
let x = 1;
let y = 2;

console.log(x === 1 && y === 1); //結果：false
console.log(x === 1 || y === 1); //結果：true
```
また、以下の値はJavaScriptではfalseと処理されます。

・空文字列("")

・数値の0、NaN(Not a Number)

・null、undefined

###ショートカット演算
論理積、論理和演算子を利用する場合に気をつける必要があるのがショートカット演算です。
例えば、論理積の場合には左式がfalseと評価された時点で条件式全体が必ずfalseとなるので、**右式の評価は実行されません。**
これをショートカット演算と呼びます。
このショートカット演算から以下の1と2は意味的に同じになります。

```javascript
if(x === 1){console.log('こんにちは');} //...1
x === 1 && console.log('こんにちは') //...2
```
しかし、2の書き方はおすすめしません。
というのも、右式が実行されるかどうかが曖昧になり、思わぬバグの温床になりかねないからです。
そのため、基本的に論理演算子の後ろに、関数の呼び出し、インクリメント/デクリメント演算子、代入演算子等の**値を実際に操作するような式を含めるのは避けましょう。**

とはいっても、**ショートカット演算にも使いどころ**があります。

```javascript
let msg = '';
msg = msg || 'こんにちは、世界！';
console.log(msg); //結果：こんにちは、世界！
```
上記のように変数msgがfalsyな値の場合にデフォルト値として「こんにちは、世界！」を代入できます。

##ビット演算子
ビット演算は、**整数を2進数で表現したときに各桁に対して論理計算を行う演算のことをいいます。**
また、ビット演算子は**ビット論理演算子**と**ビットシフト演算子**の2つに分けることができます。

###ビット論理演算子

```
10進数  2進数　 10進数
6       0110
3　　　 &0011
      -------
        0010　→　2
```
論理積では上記のようにどちらのビットが1（true）の場合には1、どちらかのビットが0(false)の場合には0になります。

そして、**否定演算子**の結果は少し複雑です。

```
10進数  2進数　 10進数
10      1010
      -------
        0101　→　-11
```
結果が「-11」になるのに疑問を抱く方もいらっしゃるでしょう。
これは、否定演算子が正負を表す符号も反転させているためです。
2進数で負数を表す際には**「ビット列を反転させて1を加えたものが絶対値になる」**というルールがあります。
そのため、上記の例だと「0101」を反転させた「1010」に1を足した「1011」がその絶対値になり、返ってくる値が「-11」になるというわけです。

###ビットシフト演算子

```
10進数  2進数　 10進数
10      1010
      ------- <<1
       10100　→　20
```
上記は左ビットシフト演算子を使用した場合の例です。
ビットシフト演算子は**その桁を指定された桁だけ、左もしくは右にシフトします。**
上記は、左シフトであるため右側の桁は「0」で埋められます。
その結果「10100」になり、10進数に変換した「20」となります。

##その他の演算子
###delete演算子
delete演算子は**オペランドに指定した変数や配列の要素、オブジェクトのプロパティを削除します。**
削除に成功した場合にはtrue、失敗した場合にfalseを返します。

```javascript
let ary = ['JavaScript', 'Ajax', 'ASP.NET'];
console.log(delete ary[0]); //結果：true
console.log(ary); //結果：[1:"Ajax", 2:"ASP.NET"]・・・①

let obj = {x:1, y:2};
console.log(delete obj.x); //結果：true
console.log(obj.x); //結果：undefined

let obj2 = {x:obj, y:2};
console.log(delete obj2.x); //結果：true
console.log(obj); //結果：{y: 2}・・・②

let data1 = 1;
console.log(delete data1); //結果：false
console.log(data1); //結果：1・・・③

data2 = 10;
console.log(delete data2); //結果：true
console.log(data2); //結果：エラー（data2は存在しない）
```
上記の結果から以下のことがわかります。

①配列の要素を削除した場合には、指定した**要素が削除されるだけで後ろの要素が繰り上がるわけではない**（インデックス番号は変化しない）

②プロパティを削除した場合にも、**プロパティそのものが削除されるだけで、プロパティが参照するオブジェクトが削除されるわけではない。**

③明示的に宣言された変数を削除することはできない。

###typeof演算子
typeof演算子は、オペランドに指定した変数やリテラルのデータ型を表す文字列を返します。
以下が実行例です。

```javascript
let num = 1;
console.log(typeof num); //結果：number

let str = 'こんにちは';
console.log(typeof str); //結果：string

let flag = true;
console.log(typeof flag); //結果：boolean

let ary = [1,2,3];
console.log(typeof ary); //結果：object

let obj = {x:1, y:2};
console.log(typeof obj); //結果：object
```
typeof演算子では配列もオブジェクトも「object」が返ってくることを覚えておきましょう。

##まとめ
今回は演算子について記述しました。
演算子はなんとなく利用していましたが、いろいろルールがあり、これらを把握しておかないと思わぬバグにつながるかもしれません。
これからは今回記述したことを理解した上で使用していこうと思います。