ES5とES6の違いをまとめてみた(メモ)

個人的なメモ。ときどき追記していく。

## テンプレートリテラル
テンプレートリテラル（`` `hoge` ``）を使用することで、特殊文字のエスケープや連結演算子の記述などが不要になる。
また、テンプレートリテラル内では、`${variable}`で変数をそのまま記述することができる。

**ES5**

```
var name = "hoge";
var age = 13;
console.log("My name is \"" + name + "\" and I'm " + age + " years old.");
// My name is "hoge" and I'm 13 years old.
```

**ES6**

```
var name = "hoge";
var age = 13;
console.log(`My name is "${name}" and I'm ${age} years old.`);
// My name is "hoge" and I'm 13 years old.
```

## アロー関数

### ES5における関数宣言
ES5における関数宣言の方法は3種類存在する。

**通常のfunction命令**

```
function str(arg1, arg2) {
  console.log('hoge');
}
```

**Functionコンストラクター（あまり使用しない）**

```
var str = new Function("arg1", "arg2", "console.log('hoge')");
```

**関数リテラル（匿名関数を利用）**

```
var str = function(arg1, arg2) {
  console.log('hoge');
}
``` 

### アロー関数の宣言

ES6では、アロー関数を利用することで関数リテラルをシンプルに記述できる。
なお、関数リテラルとは、後述の`this`の挙動が異なる。 

```
var str = (arg1, arg2) => {
  console.log('hoge');
}
```

アロー関数は、引数が1つの場合に限って、

    var str = arg1 => console.log(arg1);

のように引数の`()`を省略して書くことができる（引数が0個の場合は省略できない）。
また、上記の例のように1行に収まる場合、`{}`も省略してよい。

アロー関数がオブジェクトリテラルを返す場合は、

    var str = func => ({ id: '8' });

のように、全体を`()`で囲う必要がある。

### thisの挙動の違い

#### (1) メソッド呼び出し

    var obj = {
      value: 10,
      show: function() {
        console.log(this.value); // 10
      }
    }
    obj.show();

オブジェクトに関連づけられた関数であるメソッドにおいては、`this`は呼び出し元となるオブジェクトを意味する。
すなわち、上記の場合の`this`は`obj`であり、`this.value`は`obj.value`を指す。

#### (2) 関数呼び出し

    function func() {
      var value = 2;
      console.log(this.value) // undefined
    }
    func();

上記のように通常の関数を呼び出した場合には、（オブジェクト内で宣言した関数だとしても）`this`はグローバルを指す。
このため、関数内の`this.value`は関数内のvalue値を参照せず、グローバルのvalue値(未定義)を見る。関数外に`var value = 1;`と書けば、結果は`1`となる。

#### (3) コンストラクタ呼び出し

    function Obj(value) {
      this.value = value;
    }
    var obj = new Obj(0);
    console.log(obj.value); // 0

コンストラクタでインスタンス化している場合、`this`は生成されたインスタンスを指す。
今回の場合は、`this`は`obj`を指す。

#### (4) apply, call, bind

    var obj = {
      value: 1,
      show: function() {
        console.log(this.value);
      }
    };
    var newObj = {
      value: 5
    };
    obj.show(); // 1
    obj.show.call(newObj); // 5
    obj.show.apply(newObj); // 5

`call``apply`メソッドを用いることで、第1引数の値をthisに束縛することができる。
すなわち、`newObj = { value: 5 }`が`this`であるため、`this.value`の値は`5`に変化する。  
第2引数以降は関数に渡される引数。`call`は順番通りに、`apply`は配列として渡す。また、`bind`メソッドを使った場合は以下のように書く（`this`を束縛した関数を生成する）。

    var newFunc = obj.show.bind(newObj);
    newFunc();

#### (5) アロー関数 [ES6〜]
アロー関数では、関数が宣言されたときのスコープに`this`が自動的に束縛される。

    var obj = {
      value: 10,
      
      //メソッド呼び出し
      show: function() {
        console.log(this.value); // 10
        
        // 関数呼び出し
        function show_01() {
        	console.log(this.value); // undefined
        }
        show_01();
        
        // アロー関数
        var show_02 = () => {
        	console.log(this.value); // 10 thisはobjを参照
        }
        show_02();
      }
    }
    obj.show();


## 変数の宣言

### var, let, const

ES6では、従来の`var`に加えて新しく`let``const`というシンタックスを使えるようになった。

`let` …… 変数の再宣言ができない
`const` …… 変数への再宣言と再代入ができない

`var`を使った変数宣言では、例えば以下のような場合でもエラーを引き起こさない。

```
var x = 10;
x = 15;
console.log(x); // 15
var x = 12;
console.log(x); // 12
```

`let`を用いた場合、以下のように一度宣言された変数と同名の変数を再度宣言することができない（変数への再代入はできる）。

```
let x = 10;
x = 15;
console.log(x); // 15
let x = 12; // Identifier 'x' has already been declared
```

また、`const`は定数のような役割をしており、一度初期化した変数への再代入が認められない。

```
const x = 10;
console.log(x); // 10
x = 15; // TypeError: Assignment to constant variable.
```

ただし、`const`で参照型(オブジェクトや配列)を宣言した場合、参照自体の差し替えはできないが、参照の中身の変更は可能である。

```
var ary1 = () => {
	const aryFalse = [1, 2, 3];
	aryFalse = [4, 5, 6]; // Error
	console.log(aryFalse);
}
ary1(); // Error

var ary2 = () => {
	const aryTrue = [1, 2, 3];
	aryTrue[1] = 10;
	console.log(aryTrue);
}
ary2(); // [1, 10, 3]
```

### 変数のブロックスコープ
`let``const`をifやforなど{}で囲まれたブロック内で宣言した場合、それらの変数をブロックの外側で参照することはできない。

```
if(true) {
	var i = 0;
}
console.log(i); // 0

if(true) {
	let j = 10;
}
console.log(j); // ReferenceError

if(true) {
	const k = 100;
}
console.log(k); // ReferenceError
```

なお、ブロック外で`let``const`を用いて宣言した変数(定数)を、ブロック内から参照することはできる。

```
const i = 5;
if(true) {
	sonsole.log(i); // 5
}
```


## モジュール
ES5以前では、機能ごとに各JSファイルに分割して管理・開発することが基本的には出来なかった。しかし、ES6では、別のファイルをインポートすることができる。

> （追記）名前空間パターンである`var obj = obj || {}`を利用して出来るらしい。
>
>* [JavaScript モジュール・クラス総括（2015年7月）](http://qiita.com/nowri/items/3ebadf71bbdfacf186aa "Title")

### require

	js/ ----- script.js 
	      |
	      --- slider.js 

といったファイル構成である場合、従来であればHTML上で

```HTML:index.html
<script src="slider.js"></script>
<script src="script.js"></script>
```

と書いていたところを、

```JavaScript:script.js
var slider = require(./slider.js);
```
	
とすることで、`slider.js`をインポートできる（`./slider`と記述してもよい）。
このように、`require`を用いることで、ファイル自体を読み込むことができる。

> （追記）requireはES5の頃からCommonJSの仕様として存在していたとのこと。ただし、ブラウザではいまだ(`browserify`等を用いた変換処理なしでは)未対応。
> 
>* [旧石器時代のJavaScriptを書いてる各位に告ぐ、現代的なJavaScript超入門 Section2 ～CommonJSモジュールと仲良くなろう～](http://qiita.com/gaogao_9/items/190f4b52d6876f0b8d8a#%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%A8%E3%81%84%E3%81%86%E5%8D%98%E4%BD%8D%E3%81%AB%E5%88%86%E3%81%91%E3%82%8B%E3%81%A8%E3%81%84%E3%81%86%E3%81%93%E3%81%A8%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6-scissors "Title")

### import/export

モジュールはある機能を実現するためのプログラムのかたまりのことで、別ファイルに渡す変数/関数などのまとまりのことを言う。
後述するクラスなど、モジュールを読み込む際は、`import`と`export`を利用することで実現できる。

#### モジュールを1つだけ受け渡しする場合

**モジュールの読み込み**
`import 'import先の名前' from 'ファイルのパス'`

**モジュールの出力**
`export default 'モジュール'`

```JavaScript:script.js
import Carousel from './carousel';
const carousel = new Carousel();
```

```JavaScript:carousel.js
export default class Carousel {
	constructor() {
		this.calc();
	}
	calc() {
		console.log(10);
	}
}

```

#### モジュールを複数受け渡しする場合

出力側では受け渡ししたいものに`export`を付けておき、import側は
`import {受け取り1, 受け取り2, ...} from 'ファイルのパス'`
のように記述する。

```JavaScript:script.js
import {multi, superMulti} from './multiply';
console.log(multi(5)); // 50
console.log(superMulti(6)); // 600
```

```JavaScript:multiply.js
export const i = 10;

export function multi(x) {
	return i * x;
}

export function superMulti(x) {
	return i * x * 10;
}
```

なお、すべてのモジュールを受け渡しする場合、import側で
`import * as 'オブジェクト名' from 'ファイルのパス'`
と書けばよい。
上記の例の場合は、以下のように書く。 

```JavaScript:script.js
import * as lib from './multiply';
console.log(lib.multi(5)); // 50
```

## クラス

### クラスの宣言

ES5ではprototypeを利用して実現していたクラス定義を、ES6ではclass命令を導入することで書けるようになった。

**ES5**

```
var Add = function(arg1, arg2) {
	this.arg1 = arg1;
	this.arg2 = arg2;
};

Add.prototype.calc = function() {
	return this.arg1 + ' + ' + this.arg2 + ' = ' + (this.arg1 + this.arg2);
};

var num = new Add(5, 8);
console.log(num.calc()); // 5 + 8 = 13
```

**ES6**

```
class Add {
	constructor(arg1, arg2) {
		this.arg1 = arg1;
		this.arg2 = arg2;
	}
	calc() {
		return this.arg1 + ' + ' + this.arg2 + ' = ' + (this.arg1 + this.arg2);
	}
}

var num = new Add(5, 8);
console.log(num.calc()); // 5 + 8 = 13
```

### クラスの継承

クラスの継承やオーバーライドは、`super`を使って行えるようになった。

**ES5（すでにAddを宣言している前提）**

```
var AddSquare = function(arg1, arg2) {
	Add.call(this, arg1, arg2);
};

Object.assign(AddSquare.prototype, Add.prototype);

AddSquare.prototype = {
	calc : function() { // メソッドの省略不可
		Add.prototype.calc.call(this);
	},
	calcSquare : function() {
		this.pow = Math.pow(this.arg1 + this.arg2, 2)
		return '(' + this.arg1 + ' + ' + this.arg2 + ')^2 = ' + this.pow;
	}
};

var numSquare = new AddSquare(5, 8);
console.log(numSquare.calc()); // 5 + 8 = 13
console.log(numSquare.calcSquare()); // (5 + 8)^2 = 169
```

**ES6（すでにAdd classを宣言している前提）**

```
class AddSquare extends Add {
	constructor(arg1, arg2) {
		super(arg1, arg2);
	}
	calc() { // メソッドごと省略可
	   super.calc();
	}
	calcSquare() {
		this.pow = Math.pow(this.arg1 + this.arg2, 2)
		return '(' + this.arg1 + ' + ' + this.arg2 + ')^2 = ' + this.pow;
	}
}

var numSquare = new AddSquare(5, 8);
console.log(numSquare.calc()); // 5 + 8 = 13
console.log(numSquare.calcSquare()); // (5 + 8)^2 = 169
```