酒飲みがJavaScriptで個人的に気をつけている所

#はじめに

友人がJavaScript書いてて~~辛そう~~楽しそうなので、私が書いてて気をつけている点とかその他引っかかりそうな所をまとめてみた
また実行環境は Windows10 / GoogleChrome 最新版


#バージョン

前提としてJavaScriptのバージョンをどうするか
ES6ではクラス構文とかが追加されてだいぶ書きやすくなった
一方ES5までしか対応していない環境もある

とはいうものの、メジャーなブラウザはほぼES6に対応している
またどうしてもES5が必要な場合でも、Babel先生に頼めばES5に落としてくれる

[Babel](https://babeljs.io/)

という訳でES5で書く必要はほぼ無いと思う
以降ES6を前提に書いていく


#変数宣言

ES5まではvarしか無かった
ES6でconstとletが登場したので、これらの違いについて見てみる

```javascript

function sake() {
    sake0 = 'Shinomine';
    var sake1 = 'Kujira';
}

sake();
console.log(sake0); // Shinomine
console.log(sake1); // sake1 is not defined
```

varが無いとグローバルスコープになる
varを付けるとスコープは関数内

次にletについて

```javascript

if (true) {
    var sake2 = 'Tsukasabotan';
}

if (true) {
    let sake3 = 'Senchuhassaku';
}

console.log(sake2); // Tsukasabotan
console.log(sake3); // sake3 is not defined
```

varでもifは通り抜けるが、letはifの中にスコープが収まっている
もう一つvarとletの違いがある

```javascript

var sake4 = 'Harushika';
var sake4 = 'Akishika';
console.log(sake4); // Akishika

let sake5 = 'Katanosakura';
let sake5 = 'Michizakura'; // Identifier 'sake5' has already been declared
```

varは上書き宣言できるがletはできない
なおletとconstは値が上書きできるかどうかの違いだけ
以上より、letとconstだけで済ませるべきだと思う


#比較

等価演算子には==と===の二つがある
後者のほうが厳密であるが、実際どう違うか少し見てみる

```javascript

1 == 1 // true
1 == '1' // true
1 == true // true
```

こちらは大体等しければtrueを返す

```javascript

1 === 1 // true
1 === '1' // false
1 === true // false
```

こちらは厳密に一致した場合だけtrueを返す

前者は左右を同じ型に変換してから等しいかどうかを調べているらしい
後者なら形が違った時点でfalseを返す
なお、否定の場合は!=と!==のようになる

気をつけたい比較の一つにNaNがある

```javascript

NaN === NaN // false
```

NaN同士の比較はfalseになる
この場合Number.isNaNを使う

```javascript

Number.isNaN(NaN) // true
```

なお、Number.isNaNはES6から実装された
それ以前はisNaNを使用する

が、これがまた問題がある

```javascript

Number.isNaN(null) // false
Number.isNaN(undefined) // false

isNaN(null) // false
isNaN(undefined) // true
```

isNaN(undefined)がtrueを返しているので注意


#nullとundefined

似ているようで違う
まず比較すると以下のようになる

```javascript

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

やはり似て非なるらしい
では何が違うのかを見ていこう
まずはundefinedから

```javascript

let sake;
console.log(sake); // undefined
sake = 'Akabu';
console.log(sake); // Akabu
console.log(sake.alcohol); // undefined
```

宣言だけされた変数にはundefinedが入っている
同じく初期化していないプロパティもundefined

```javascript

function manotsuru() { }
console.log(manotsuru());
```

何もreturnしない関数の場合、戻り値がundefinedになる
returnだけ書いた場合も同様

このように、undefinedとはまだ作られていない状態、つまり未定義である

一方のnullだが、これは明示的にnullを使用しないと発生しない

```javascript

let sake = 'Daimon';
console.log(sake); // Daimon
sake = null;
console.log(sake); // null
```

一度定義したものを空っぽにするときに使う
ニュアンス的な違いになるが、nullは「空っぽ」、undefinedは「未定義」と考えると良いと思う

ではこれらの使い分けをどうするかだが…実際の所あまり違いはないと思う
ただし、nullは明示的な空であるのに対して、undefinedは自然発生的な空とも取れる
そのため、変数を空にする目的でundefinedを代入すると以下のようになる

```javascript

let sake0 = 'Takesuzume';
let sake1 = 'Gassan';

// ---- この間非常に長いコード ----

sake0 = undefined;
sake1 = null;

// ---- この間非常に長いコード ----

console.log(sake0); // undefined
console.log(sake1); // null
```

前者は空なのだが、これが単なる未定義なのか、使用した後に消されたのかがわからない
後者は明示的に空になったのがわかる
以上より、明示的に空にするならnullを使用するべきで、undefinedを代入するのは避けたほうが良いと思う

同じことは関数にも言える

```javascript

function okura(canGet) {
    if (canGet) return 'Okura';
    return undefined;
}

function sennorikyu(canGet) {
    if (canGet) return 'Sennorikyu';
    return null;
}

console.log(okura(false)); // undefined
console.log(sennorikyu(false)); // null
```

こちらも後者なら明示的に空だとわかるだろう


#名前空間

javascript に名前空間なんてものはない
だが、それ風味なものは実装できる
一つだけグローバルなオブジェクトを用意しておいて、残りはそいつのプロパティとして実装する方法である

```javascript

Sake = { };

Sake.Dewazakura = class { } // OK
Sake.Kokushimusou = class { } // OK
```

なお、名前空間自体が競合した場合を考えて以下のように書くのが良いらしい

```javascript

var Sake = Sake || { };
```

この場合はvarでないとエラーになるようだ


#クラス宣言

クラスの宣言方法は二種類ある

```javascript

class Ippongi { }
Suginishiki = class { }
```

これだけならほとんど違いはない
しかし、先述の名前空間の兼ね合いで、後者の書き方をしたいと思う

```javascript

var Sake = Sake || { };

class Sake.Bijofu { } // Unexpected token .
Sake.Urakasumi = class { } // OK
```

もちろん関数も同じ

```javascript

function Sake.Umenoyado() { } // Unexpected token .
Sake.Shirakiku = function() { } // OK
```


#関数を返す

```javascript

Box = class {
    constructor() {
        this.sake = ['Reisen', 'Suigei', 'Tengumai'];
    }

    show() {
        this.sake.forEach(a => console.log(a));
    }

    getShow0() {
        return this.show;
    }

    getShow1() {
        return () => this.show();
    }

	getShow2() {
        return function() { this.show() };
    }
}

const box = new Box();
box.show(); // Reisen Suigei Tengumai
```

getShowを三つ用意した
一つ目は普通にメソッドを返す、二つ目は無名関数で包んで返す
三つ目は後述する

```javascript

const boxShow0 = box.getShow0();
boxShow0(); // Cannot read property 'sake' of undefined
```

メソッドをそのまま返すと実行できない

```javascript

const boxShow1 = box.getShow1();
boxShow1(); // Reisen Suigei Tengumai
```

こちらは通る
メソッドを返すだけでは実行できず、メソッドを実行する関数を返す必要がある

しかしこれが可能なのはアロー関数を使用した場合に限る
ここで、アロー関数ではなく普通の関数宣言した場合の三つ目のshowを見てみる

```javascript

const boxShow2 = box.getShow2();
boxShow2(); // Cannot read property 'show' of undefined
```

これはアロー関数の特性の一つで、アロー関数は宣言した時点でthisの値が確定する
逆に言うと、getShow0やgetShow2のthisは宣言した時点で確定していない
これらは呼び出された時点でその左辺を見て決定されるらしい
つまりboxShow0やboxShow2は左辺が無いためthisがおかしくなったようだ（この時のthisはグローバルオブジェクトを指している…はず　あまり確かでない）


#あとがき　前編

まだまだ書ききれないほど罠が散らばっているが、今回はここらへんで終わりとしておく
全部書いているとあまりにも多すぎるので、特に引っかかる可能性が高そうなポイントに絞った

なお、敢えて最後に説明すると、私見が混ざっている文章の語尾には「思う」と付けた
あくまで私はこう思ったという部分である
正直それ以外の部分はググればいくらでも出てくるし、この記事はその寄せ集めに過ぎない
だが、この「思う」部分は私にしか語れない
なので、「思う」の部分を取捨選択しながら読んでいただけるとこの記事の意味が出てくると思う

ちなみにこの説明を最後に持ってきたのは、一回目読んでる最中に余計な思考を入れて欲しくなかったからだ
読み終わった上で取り入れるかどうかもう一度考えてほしい


#あとがき　後編

このソースコードはノンフィクションですが、実在する酒とは関係ありません
関係ありませんが、**酒は美味いです**