JS初心者がハマったところ

自分の復習も兼ねて、ハマったところや学んだことなどまとめてみました。

# はじめに

**（！）注意：ES2015以上前提で書いています**

最近はBabalとかPolyfill使ってES2015以上の文法が使える環境で開発することが
多いと思うので大丈夫かなと思いますが、
何らかの理由でES2015以上で書けないという方にはすみません:pray:

そもそも ES2015 とは？という方は、以下の記事が参考になるかも

> - [今日から始める ES2015(ES6) - Qiita](https://qiita.com/ngk-works/items/bf8a8318fc0acad4d43d)
- [ES2015 についてまとめ - Qiita](https://qiita.com/choro/items/952cc37ac71843988bc6)
- [ES2015再入門 - Qiita](https://qiita.com/shosho/items/a93644b297b2e3ced8a5)

# 変数の宣言

ES2015から `var` に加えて新たに `const` `let` を使って変数を宣言できるようになりました。
それぞれの宣言方法の特徴をざっと挙げます。

> - var
  - 再宣言、再代入 可
  - 関数スコープ
  - 変数の巻き上げが起こる、参照可能

> - let
  - 再宣言 不可
  - ブロックスコープ
  - 変数の巻き上げが起こるが、参照不可能

> - const
  - 再宣言、再代入 不可
  - ブロックスコープ
  - 変数の巻き上げが起こるが、参照不可能
  - 配列やオブジェクトの場合は、中の要素やプロパティの書き換えは可能 

基本的に、変数に何度も再代入する必要のある機会はほとんどないでしょう。
変数はできる限りスコープを小さくすることで可読性の向上やバグの抑制につながりますので、
for文のイテレータ変数（iとか）や、どうしても再代入が必要なときだけ `let` 、
それ以外は基本的に `const` を使うで問題ないと思います。

# 配列の繰り返し処理

JSには配列の繰り返し処理を行う構文がいくつか存在します。
通常の用途であれば `for ... of` を使うのがよさそうです。

```js

const fruits = [
  { name: 'みかん', price: 100 },
  { name: 'りんご', price: 200 },
  { name: 'バナナ', price: 300 }
]

for(let item of fruits) {
  console.log(item.name) // => みかん , りんご, バナナ
}
```

配列等の繰り返し処理を行う構文として、`forEach`というメソッドも存在します。
こちらも比較的新しい構文ですが、 **async/awaitやbreak文が使えません**。
また、`forEach`の場合関数呼び出しを挟むためパフォーマンス的にも微妙です。

※ async/await については今回割愛します。 **Promise** で調べてみてください。
# 配列の操作（破壊的/非破壊的）

配列の操作には、
**破壊的なもの（元の配列に影響を与えるもの）**と
**非破壊的なもの（元の配列に影響を与えないもの）**とがあります。

こちらの記事にわかりやすくまとめてくださっていますので、
ぜひ参考にして適切な処理をしてください。

> - [JavaScriptの配列の使い方まとめ。要素の追加,結合,取得,削除。- Qiita](https://qiita.com/takeharu/items/d75f96f81ff83680013f)

# 配列・オブジェクトのコピー

JSでは、 **配列・オブジェクト** を他の変数に直接代入した場合は参照渡しとなります。

配列は、他の言語でも参照渡しとなることが多いと思うので意識できるとして、
オブジェクトも参照渡しとなるので注意が必要です。

たとえば、次のように素直にオブジェクトをコピーすると...

```js:NG

const obj = {
  key: 'value'
}
const copy = obj
copy.key = 'hoge'

console.log(obj.key) // => 'hoge'
console.log(copy.key) // => 'hoge'
```
もとのオブジェクトの値も書き換わってしまいました。

こうしましょう。

```js:OK

const obj = {
  key: 'value'
}
const copy = { ...obj }
copy.key = 'hoge'

console.log(obj.key) // => 'value'
console.log(copy.key) // => 'hoge'
```

`...obj` は **スプレッド演算子** と呼ばれる記法で、ES2018から使えます。`※1`
ちなみに配列もスプレッド演算子を使って同じようにコピーできます。

**しかし、注意してください。**
**ネストされた配列・オブジェクトはスプレッド演算子でコピーできません。**

```js:配列

const ary = [
  { key: 'value' }
]

const copy_ary = [ ...ary ]
copy_ary[0].key = 'hoge'

console.log(ary[0].key) // => hoge
console.log(ary_copy[0].key) // => hoge

```

```js:オブジェクト

const obj = {
  child: { key: 'value' }
}

const copy_obj = { ...obj }
copy_obj.child.key = 'fuga'

console.log(obj.child.key) // => fuga
console.log(obj_copy.child.key) // => fuga
```

スプレッド演算子によるコピーも、結局はシャローコピー（浅いコピー）ですので、
入れ子のオブジェクトまではコピーできないんですね...

複雑にネストされた配列・オブジェクトをコピーする時は、
[loadash](https://lodash.com/docs/#cloneDeep) などのライブラリを使うと便利です。

スプレッド演算子については、こちらの記事が参考になります。

>- [JSのスプレッド演算子を理解する - Qiita](https://qiita.com/akisx/items/682a4283c13fe336c547)
- [【JavaScript】スプレッド演算子の便利な使い方まとめ -Qiita](https://qiita.com/Nossa/items/e6f503cbb95c8e6967f8)

# 文字列の中に変数を埋め込む

変数と文字列を結合して出力する場合、普通に書くとこうなります。

```js:NG

const name = '田中'
console.log('ようこそ' + name + 'さん')  // => ようこそ田中さん
```

普通に動くし、絶対ダメ！というわけではないのですが、
せっかくES2015が使える環境なら、
新しく導入された**テンプレートリテラル**という記法を使った方が
すっきり書けていいと思うんです。

```js:OK

const name = '田中'
console.log(`ようこそ${name}さん`)  // => ようこそ田中さん
```
テンプレートリテラルは文字列を`バッククォート`で囲うと使用できます。
このなかで変数を `${}` で囲うと文字列として展開してくれます。

以下の内容でテンプレートリテラルを使っているので、
なんだこれ？とならないように一応説明も兼ねて書いておきました...:bow:

# thisの扱い（アロー関数を使う）

> 一般的にJavaScriptでは、関数の中でthisを使うと、その呼び出し元のオブジェクトになります。
[【JavaScript】アロー関数式を学ぶついでにthisも復習する話](https://qiita.com/mejileben/items/69e5facdb60781927929)

そうなんです。
と言われてもよくわかりませんね。
上の引用元の例を少し変えて説明したいと思います。

```js

const hoge = {
  message: 'message',
  printMessage: function () {
    console.log(`log1: ${this.message}`)
    setTimeout(function(){
      console.log(`log2: ${this.message}`)
    }, 1000)
  }
}

hoge.printMessage() // => log1: message, log2: undefined
```

あれ？ log2 のほうではmessageが`undefined`になっている...？

結論から言うと、 
log1の時点での`this`は`hogeオブジェクト` 、
log2の時点での`this`は`windowオブジェクト` (ブラウザで実行した場合)だからです。

`setTimeout`は windowオブジェクト のメソッドですので、
**呼び出し元のオブジェクト ＝ this**はwindowオブジェクトになる訳ですね。

ではどうすればsetTimeoutのコールバック関数のなかでも `message` を参照できるでしょうか？
まず、古いやり方です。

```js:古いコード

const hoge = {
  message: 'message',
  printMessage: function () {
    console.log(`log1: ${this.message}`)
    const self = this
    setTimeout(function(){
      console.log(`log2: ${self.message}`)
    }, 1000)
  }
}

hoge.printMessage() // => log1: message, log2: message
```

`this`を変数(self)に入れてあげれば、なんとか参照できます。
しかし、ES2015から取り入れられた**アロー関数**を使えば、もっとスマートに書けます！
上のコードを、アロー関数を使って書くとこうなります。

```js:新しいコード

const hoge = {
  message: 'message',
  printMessage: function () {
    console.log(`log1: ${this.message}`)
    setTimeout(() => { // <- ここに注目
      console.log(`log2: ${this.message}`)
    }, 1000)
  }
}

hoge.printMessage() // => log1: message, log2: message
```

**アロー関数は、宣言された時点でthisの値を確定する**ので、
コールバック関数の中でもlog1の時点と同じthisを参照することができました。

アロー関数については、詳しくは上の引用元の記事をご覧ください。

thisは初心者がハマりやすい箇所なので、意識して使いこなせるようになりたいところです。
ES2015の構文が使用できるのであれば、アロー関数がすっきり書けてベターでしょう。

# おわりに

とりあえず思いついたものを挙げてみましたが、
他にも何か思いついたら書き足していきたいと思います。

ECMAScriptも毎年新仕様が策定されていますし、
キャッチアップしながら今後もよりよいコードを書いていけるよう精進したいと思います。

こうした記事を書いておきながら、自分もまだまだ駆け出しですので、
これも意識しておくといい、などのご意見等ありましたら
コメントにてご指摘いただけますと幸いです。

宜しくお願い致します。

# 注釈
`※1` 配列にはES2015から、オブジェクトにはES2018から使用できるようです。