僕のJavascriptの初耳な仕様

# 筆者と当記事を書き始めた経緯について
当方、資格集めが趣味のエンジニアです。
資格の保有数は20種類に到達したんですが、いまだにJS関連の資格は持っていないし、仕事で`Vue.js`を書いたりすることもあるものの、JSがそんな得意なわけでもないといったところでJS系の資格の取得を検討しております。
一応、今狙ってるのは[JS InstituteのJSE – Certified Entry-Level JavaScript Programmer Certification](https://js.institute/jse-certification)という資格です。
こちらはめちゃくちゃ初学者向けの資格なんですが、受けられる言語が英語しかないので、英語のお勉強も兼ねてひとまずこの資格をターゲットとすることにしました。

JS自体は個人的な感覚にもなりますが、そこまで特殊な言語ではない（筆者が一番多用するのはJavaなので、無意識にJavaと比較してます）と思うんですが、それでも
「こんな仕様があるんだなぁ」
と勉強しながら思うことがあるので、忘れないうちに記載していこうと思います。

:::note warn
「いやいや、そんなんJS的には周知の挙動でしょ。つか、他の言語でも普通にある仕様でしょ。」と思われるかもしれませんが、ご容赦を。
:::

# 当記事について
[こちらの教材](https://edube.org/study/jse1)を使って勉強しているんですが、まだまだ教材を1周もしきってないので、初見の仕様に出くわす度に当記事は更新していこうと思います。

# 初見だったJavascriptの仕様
## Hoisting
※[Hoisting MDN](https://developer.mozilla.org/ja/docs/Glossary/Hoisting)
`Hoisting`、和訳すると**巻き上げ**となるそうですが、これは
`varで変数を宣言した場合に、その変数の宣言は当該変数の属するスコープの先頭にリライトされること`だそうです。

なので例えば、
下記の`hoisting1.js`と`hoisting2.js`は両方とも`weight`を参照した際に`ReferenceError`を発生させるように見えますが、

```javascript:hoisting1.js
var height = 180;
console.log(height);
console.log(weight);
```

```javascript:hoisting2.js
var height = 180;
console.log(height);
console.log(weight);
var weight = 70;
console.log(weight);
```
実際の挙動は異なり、`hoisting2.js`は実際に実行されるソースは下記のようになるため実際には`hoisting2.js`に限っては、`undefined`とコンソールに出力されることになります。
```javascript:hoisting2_execute.js
var height = 180;
var weight;
console.log(height);
console.log(weight);
weight = 70;
console.log(weight);
```

## 単項プラス
※[単項プラス演算子 MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Unary_plus)
皆さんは↓のソースの計算結果わかります？
```javascript:plus1.js
const str1 = 42 + "1";
```
他の言語でもそうですが、文字列と何かを+演算子にかけると計算対象の値の両方が文字列として評価されます。
なので上記の計算結果は`421`となります。
では`"1"`を数値として扱って`43`という計算結果を得るためにはどうしたらいいでしょう？

素直に考えたら
```javascript:plus2.js
const str1 = 42 + Number("1");
```
が一番パッと出てくる解だと思いますが、実は↓でも同様の解を得られます。
```javascript:plus3.js
const str1 = 42 + +"1";
```

僕は最初見たとき意味が分からなかったんですが、こちらはJavascriptの**単項プラス演算子**による働きのためによるものです。

単項プラス演算子は直後の値を`Number`型に変換します。
そのため`"1"`が`Number`型になることで`43`という解を得ることができるようになります。

因みに`+"あ"`みたいな`Number`に変換できない値を引数として受けた場合は`NaN`が戻り値となります。

## Boolean型の複合代入演算子
他の言語でもよくある仕様なのか、これまで実務で使ったことないからわからないんですが、複合代入でよく使うのは数値型（Javascriptなら`Number`）での複合代入だと思います。
```javascript:compoundAssignment1.js
let compoundAssignmentNum = 1;
    compoundAssignmentNum += 1 // compoundAssignmentNum === 2
```
これは実務でも8000万回くらいつかってきたんですが、同様に複合代入が`Boolean`でも行えます。
```javascript:compoundAssignment2.js
let compoundAssignmentBool = true;
compoundAssignmentBool &= false; // compoundAssignmentBool === false

// もちろんショートサーキットもいける
let compoundAssignmentBool = true;
compoundAssignmentBool &&= false; // compoundAssignmentBool === false

// ORも同様
let compoundAssignmentBool = true;
compoundAssignmentBool ||= false; // compoundAssignmentBool === true
```

## 論理積、論理和の戻り値
※[論理積 MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Logical_AND)
※[論理和 MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Logical_OR)
`&&`（論理積）や`||`（論理和）の戻り値は
- `&&`：両方のオペランドが`true`の場合に`true`を返却する、その他の場合は`false`を返却する。
- `||`：両方のオペランドが`false`の場合に`false`を返却する、その他の場合は`true`を返却する。

と認識していましたが、正確には挙動は少し違くて

- `&&`：最初に遭遇したfalsyなオペランドの値を返却し、すべてがtruthyな値の場合には最後のオペランドの値を返却する。
- `||`：最初のオペランドがtruthyな値の値の場合には、最初のオペランドの値を返却する。そのほかの場合は、2つ目のオペランドの値を返却する。

「いやいや、元々の解釈と何が違うのよ。一緒じゃん。」
と思うかもしれませんが、**truthyな値**、**falsyな値**ってのが、ミソなんですねぇ。

オペランドが`Boolean`型の値なら想定通りの挙動なんです。
```javascript:andOr1.js
console.log(true && true); // --> true
console.log(true || false); // --> true
```
しかしながら、javascriptは以下の値をfalsyな値（≒`false`）として扱います。
- false
- null
- NaN
- 0
- 空文字列 ("" または '' または ``)
- undefined


加えて、論理積、論理和が返却するのは、`Boolean`型の値ではなく、**あくまでオペランドの値**です。
なのでオペランドが`Boolean`型の値でない場合は以下のような戻り値を返却することになります。

```javascript:andOr2.js
console.log("Hello" && "World"); // --> "World"
console.log(1 && 2); // --> 2
console.log("Hello" || "World") // --> "Hello"
console.log(1 || 0); // --> 1
```

`Boolean`の`true`、`false`と`truthy`、`falsy`、ここら辺の解釈が厄介ですね。

でも今思うと、javascriptで空文字のチェックって`if(string/*string型の変数*/)`とかでやってて、javascriptで空文字は`falsy`な値だから、先ほどのif文は空文字のチェックができるみたいなことを8000万回くらいやってたから、よくよく考えてみたらこのトピックの一部は既知の内容だったかもですね。

## IIFE
※[IIFE MDN](https://developer.mozilla.org/ja/docs/Glossary/IIFE)
こちらは、Immediately Invoked Function Expression、略してIIFEです。
和名だと単純に即時実行関数式となります。
例えば↓です。

```javascript:iife1.js
(function(){
    let a = 'II';
    let b = 'FE';
    console.log(`${a}${b}`);
})();
```
関数を宣言すると同時に即時実行しています。
もちろん、アロー関数も使えます。

```javascript:iife2.js
(() => {
    let a = 'II';
    let b = 'FE';
    console.log(`${a}${b}`);
})();
```