2017年度版 細かすぎて伝わらないJavaScriptの速度の話

# 概要
コードを書いていると誰もが気になってくる「どっちのほうがパフォーマンスに優れているの？」で、眠れない日々を過ごす人達のために、比較結果をまとめてみました。

# 実行環境
OS: macOS Sierra 10.12.5
Chrome: 58.0.3029.110 (64-bit)
Safari: 10.1.1

# 計測方法
10 回実行した結果の平均値です。

# 比較
## シングルクオート VS ダブルクオート
### コード
```js
start = () => {
  const retryCount = 1000000;
  const startTime = new Date();
  for (let i = 0; i < retryCount; i = i + 1) {
    /* ダブルクオート
    const text = "abcde" + "fghij" + "klmno" + "pqrst" + "uvwxy" + "z" + i;
    */
    /* シングルクオート
    const text = 'abcde' + 'fghij' + 'klmno' + 'pqrst' + 'uvwxy' + 'z' + i;
    */
  }
  const endTime = new Date();
  console.log(endTime - startTime);
}
```

### 結果
- Chrome

| 対象 | Average | 最速 |
|:---|:---|:---:|
| シングルクオート | 256.2 ms | |
| ダブルクオート | **241.5 ms** | ◯ |

- Safari

| 対象 | Average | 最速 |
|:---|:---|:---:|
| シングルクオート | 122.9 ms | |
| ダブルクオート | **122.3 ms** | ◯ |

### 所感
>ダブルクオートのほうが若干早いという結果になりましたが、ほぼ誤差の範囲なので
>パフォーマンスを気にするよりも可読性を優先したほうが良さそうです。

## if VS switch
### コード
```js
start = () => {
  const startTime = new Date();
  for (let i = 0; i < 10000000; i = i + 1) {
    const x = 1;
    /* if
    if ( x === 1) {}
    else if ( x === 2) {}
    else if ( x === 3) {}
    else {}
    */
    /* switch
    switch (x) {
      case 1:
        break;
      case 2:
        break;
      case 3:
        break;
      default:
        break;
    }
    */
  }
  const endTime = new Date();
  console.log(endTime - startTime);
}
```
### 結果
#### 条件に一致する場合 (x = 1)
- Chrome

| 対象 | Average | 最速 |
|:---|:---|:---:|
| if | **155.5 ms** | ◯ |
| switch | 157.8 ms | |

- Safari

| 対象 | Average | 最速 |
|:---|:---|:---:|
| if | **204.2 ms** | ◯ |
| switch | 210.4 ms | |

#### 条件に一致しない場合 (x = 4)

- Chrome

| 対象 | Average | 最速 |
|:---|:---|:---:|
| if | 182.9 ms| |
| switch | **182.7** ms| ◯ |

- Safari

| 対象 | Average | 最速 |
|:---|:---|:---:|
| if | **207.7 ms** | ◯ |
| switch | 213.2 ms | |

### 所感
>if と switch の速度差は、ほぼ誤差の範囲だと思います。
>if では else 処理で速度に差異があるため、else での処理を避けることに注力したほうが良さそうです。

## インクリメント
### コード
```js
start = () => {
  const startTime = new Date();
  let x = 1;
  for (let i = 0; i < 10000000; i = i + 1) {
     /* x++; */
     /* x = (x + 1); */
     /* x = (x + 1)|0; */
  }
  const endTime = new Date();
  console.log(endTime - startTime);
}
```
### 結果
- Chrome

| 対象 | Average | 最速 |
|:---|:---|:---:|
| ```x++``` | **145.6 ms** | ◯ |
| ```x=(x+1)``` | 155.5 ms | |
| ```x=(x+1)｜0``` | 162.1 ms | |

- Safari

| 対象 | Average | 最速 |
|:---|:---|:---:|
| ```x++``` | **107.3 ms** | ◯ |
| ```x=(x+1)``` | 110.3 ms | |
| ```x=(x+1)｜0``` | 110.3 ms | |

### 所感
>色々な記事で一番速いと言われていた ```x = (x + 1)|0``` よりも ```x++``` のほうが速かったという結果になりました。
>どこかのタイミングでインクリメント処理が最適化されたのかもしれません。

## 文字連結
### コード
```js
start = () => {
  const startTime = new Date();
  const a = "abcde";
  const f = "fghij";
  const k = "klmno";
  const p = "pqrst";
  const u = "uvwxyz";
  for (let i = 0; i < 1000000; i = i + 1) {
    /* + 連結
    let text = a + f + k + p + u + i;
    */
    /* concat 連結
    let text = a.concat(f).concat(k).concat(p).concat(u).concat(i);
    */
    /* テンプレートリテラル
    let text = `${a}${f}${k}${p}${u}${i}`;
    */
    /* += 連結
    let text = a;
    text += f;
    text += k;
    text += p;
    text += u;
    text += i;
    */
  }
  const endTime = new Date();
  console.log(endTime - startTime);
}
```

### 結果
- Chrome

| 対象 | Average | 最速 |
|:---|:---|:---:|
| + 連結 | **230.1 ms** | ◯ |
| concat 連結 | 329.2 ms | |
| テンプレートリテラル | 292.2 ms | |
| += 連結 | 230.2 ms | |

- Safari

| 対象 | Average | 最速 |
|:---|:---|:---:|
| + 連結 | 177.2 ms | |
| concat 連結 | 264.6 ms | |
| テンプレートリテラル | **144.4 ms** | ◯ |
| += 連結 | 176.8 ms | |

### 所感
>Safari と Chrome で差のある結果となりました。
>concat 連結が最遅なので避けるとして、可読性を考えると変数を含む場合はテンプレートリテラルを使う方が良さそうです。


## Undefined判定
### コード
```js
start = () => {
  let x;
  const startTime = new Date();
  for (let i = 0; i < 10000000; i = i + 1) {
    /* if (typeof x === 'undefined') {} */
    /* if (x === undefined) {} */
    /* if (x === void 0) {} */
    /* if (!x) {} */
  }
  const endTime = new Date();
  console.log(endTime - startTime);
}
```

### 結果
- Chrome

| 対象 | Average | 最速 |
|:---|:---|:---:|
| ```typeof x === 'undefined'``` | 150.4 ms | |
| ```x === undefined``` | 143.5 ms | |
| ```x === void 0``` | **143.1 ms** | ◯ |
| ```!x``` | 158.1 ms | |

- Safari

| 対象 | Average | 最速 |
|:---|:---|:---:|
| ```typeof x === 'undefined'``` | 106.2 ms | |
| ```x === undefined``` | 107 ms | |
| ```x === void 0``` | 106.8 ms | |
| ```!x``` | **104.7 ms** | ◯ |

### 所感
>Chrome では一番遅かった ```!x``` が Safariでは一番速いという結果になりました。
>Safariのほうは速度は誤差の範囲だと思いますので、横着せずに ``` !x ``` 以外で判定したほうが良さそうです。

## 真偽判定
### コード
```js
start = () => {
  const startTime = new Date();
  const x = true;
  for(let i = 0; i < 10000000; i = i + 1) {
    /* if (x) {} */
    /* if (x === true) {} */
    /* if (!x) {} */
    /* if (x === false) {} */
  }
  const endTime = new Date();
  console.log(endTime - startTime);
}
```

### 結果
#### 真 判定
- Chrome

| 対象 | Average | 最速 |
|:---|:---|:---:|
| ```x``` | **161.5 ms** | ◯ |
| ```x === true``` | 189.6 ms | |

- Safari

| 対象 | Average | 最速 |
|:---|:---|:---:|
| ```x``` | 111.5 ms | |
| ```x === true``` | **110 ms** | ◯ |

#### 偽 判定
- Chrome

| 対象 | Average | 最速 |
|:---|:---|:---:|
| ```!x``` | **159.8 ms** | ◯ |
| ```x === false``` | 185.8 ms | |

- Safari

| 対象 | Average | 最速 |
|:---|:---|:---:|
| ```!x``` | 109.9 ms | |
| ```x === false``` | **107.8 ms** | ◯ |

### 所感
>Chrome と Safari で異なる結果となりました。
>Safariのほうの差は誤差なので、個人的には省略型で書きたいです。

## スコープ
### コード
```js
scope01 = 1;
class Test {
  constructor() {
    this.scope02 = 1;
  }
  start() {
    let scope03 = 1;
    const startTime = new Date();
    for(let i = 0; i < 10000000; i = i + 1) {
       /* scope01++; */
       /* this.scope02++; */
       /* scope03++; */
    }
    const endTime = new Date();
    console.log(endTime - startTime);
  }
}
start = () => {
  const test = new Test();
  test.start();
}
```

### 結果
- Chrome

| 対象 | Average | 最速 |
|:---|:---|:---:|
| scope01 | 247.3 ms | |
| scope02 | 274.6 ms | |
| scope03 | **156.9 ms** | ◯ |

- Safari

| 対象 | Average | 最速 |
|:---|:---|:---:|
| scope01 | 116.3 ms | |
| scope02 | 111.8 ms | |
| scope03 | **110.1 ms** | ◯ |

### 所感
>Chrome の scope01 と scope02 の差があまり納得できませんが、基本的にはなるべく近いスコープを利用するほうが良いということで問題ないと思います。

## New のコスト
### コード
```js
class Test {
  constructor() {}
}
start = () => {
  const startTime = new Date();
  for(let i = 0; i < 10000000; i = i + 1) {
    /* const test = 0; */
    /* const test = new Test(); */
  }
  const endTime = new Date();
  console.log(endTime - startTime);
}
```

### 結果
- Chrome

| 対象 | Average |
|:---|:---|:---:|
|```const test = 0``` | 139.6 ms |
|```const test = new Test()``` | 399.2 ms |
| 上 ２ つの差 | 259.6  ms |

- Safari

| 対象 | Average |
|:---|:---|:---:|
|```const test = 0``` | 223.9 ms |
|```const test = new Test()``` | 660.2 ms |
| 上 ２ つの差 | 436.3  ms |

### 所感
>何度も繰り返し new するケースも少ないと思うため、あまり気にしなくて良さそうです。

## Try/Catchブロック のコスト
### コード
```js
start = () => {
  const startTime = new Date();
  for(let i = 0; i < 10000000; i = i + 1) {
    /* なし
    const x = 0;
    */
    /* Try/Catch
    try {
      const x = 0;
 　  　}
    catch (exception) {}
    */
    /* Try/Catch/Finally
    try {
      const x = 0;
 　  　}
    catch (exception) {}
    finally {}
    */
  }
  const endTime = new Date();
  console.log(endTime - startTime);
}
```

### 結果
- Chrome

| 対象 | Average |
|:---|:---|
| なし | 144.5 ms |
| Try/Catch | **20.3 ms** |
| Try/Catch/Finally | 22.1 ms |

- Safari

| 対象 | Average |
|:---|:---|
| なし | 227.5 ms |
| Try/Catch | **224.3 ms** |
| Try/Catch/Finally | 225.1 ms |

### 所感
>Chrome の try/catchブロックを利用した場合が圧倒的に早すぎて意味がわかりませんでした。
>どちらの場合も try/catchで囲む方が早くなっています。
>スコープの問題でこんなに高速になっているんでしょうか...?

# 終わりに
この結果は2017年5月時点で、実行環境に表記した内容での計測結果です。
ロジックや、環境次第では　**異なる結果**　になる可能性も、十分にありますので注意してください。