いまさらきけないModern JavaScriptのまとめ

# JavaScriptの歴史（と未来）
- JavaScriptの発表 （1995/12/4）: ネットスケープ＋サン
- ES1 (1997/6): オブジェクトベース、Prototype、他
- ES3 (1999/12): 正規表現、try-catch、do-while、Stringメソッド
- Google Mapリリース (2005/2)
- Google Chromeリリース (2008/9): V8エンジン
- ES5 (2009/12): 'use strict'、JSON、function.bind、Objectメソッド、Arrayメソッド
- **ES6 (2015/6): Class構文、module、Arrow function、const/let、Template literal、Promise、他多数**
- ES7 (2016/6): 階乗記号、array.includes()
- ES8 (2017/6): async/await、メモリ操作、String padding、Objectメソッド追加、他
- [ES9 (2018/6)](http://2ality.com/2017/02/ecmascript-2018.html): [非同期イテレータ](https://qiita.com/bitrinjani/items/c63946ff1a81ee4e7e63)、BigInt、Promise.prototype.finally、import()

ES10(2019/6?)の候補: [ECMAScript Proposals](https://github.com/tc39/proposals)

**注: ECMAScriptはブラウザ搭載のJavaScriptと違う**

# ES6~8

## `const`/`let`
`var`はもう使わなくていい

|  | Scope | Reassignable | Mutable | "TDZ" |
|:-:|:-:|:-:|:-:|:-:|
|`var`|  Function | Yes  |  Yes |  No |
| `let`  | Block  |  Yes |  Yes | Yes  |
| `const`  |  Block |  No | Yes  |  Yes |

### Scope
``` js:function_scope.js
function funcScope() {
  var hoge = 'hoge';
  console.log(hoge); // -> 'hoge'
  if (true) {
    var hoge = 'hogehoge';
    console.log(hoge); // -> 'hogehoge'
  }
  console.log(hoge); // -> 'hogehoge'
}
```

``` js:block_scope.js
function blockScope() {
  let hoge = 'hoge';
  console.log(hoge); // -> 'hoge'
  if (true) {
    let hoge = 'hogehoge';
    let fuga = 'fuga';
    console.log(hoge); // -> 'hogehoge'
  }
  console.log(hoge); // -> 'hoge'
  console.log(fuga); // -> ReferenceError: fuga is not defined
}
```

### Reassignable
``` js
var a = 123;
a = 'hoge';
a = function(hoge) {console.log(hoge)};
a = {hoge: 'hoge', fuga: 12};
a = Object;

const b = 123;
b = 125; // -> TypeError: Assignment to constant variable.
```

### Mutable
``` js
const ary = [1, 2, 3];
ary.push(4); // -> [1, 2, 3, 4]

const obj = {hoge: 'fuga', foo: 'bar'};
obj.yahoo = 'google'; // -> {hoge: 'fuga', foo: 'bar', yahoo: 'google'}
```

### Temporal Dead Zone (TDZ)
``` js:var_hoisting.js
console.log(hoge); // -> undefined
var hoge = 'hoge';
```

``` js:let_hoisting.js
console.log(hoge); // -> ReferenceError: hoge is not defined
const hoge = 'hoge';
```

``` js:temporal_dead_zone.js
let a = 'out';
if (true) {
  console.log(a); // -> ReferenceError: a is not defined
    // Temporal Dead Zone
  let a = 'in';
}
```

### まとめ
- 基本的にいつも`const`を使って、再代入する変数だけ`let`を使う
- オブジェクトや配列は`const`でMutateすれば事足りる


## Arrow Function
(変数) => {処理}で関数になる

``` js:traditional_function.js
function double(x) {
  return x * 2;
}
console.log(double(2)); // -> 4
```
```js:arrow_function.js
const double = x => {
  return x * 2;
}
console.log(double(2)) // -> 4

const concat = (str1, str2) => {
  return str1 + ' ' + str2;
}
console.log(concat('hoge', 'fuga')); // -> 'hoge fuga'

const hoge = () => {
  return 'hoge';
}
console.log(hoge()); // -> 'hoge'
```

### Implicit Return
1行なら`return`がいらない

```js:implicit.js
const double = x => x * 2;
console.log(double(2)); // -> 4
```

```js:return_object.js
const getPerson = name => {name: name, job: 'teacher'}; // -> Uncaught SyntaxError: Unexpected token :

const getPerson = name => ({name: name, job: 'teacher'});
console.log(getPerson('Steve')); // -> {name: 'steve', job: 'teacher'}
```

### `arguments`がいない
```js:yes_arguments.js
function argExists() {
  return arguments.length;
}
console.log(argExists('foo', 'bar', 'hogehoge')); // -> 3
```
```js:no_arguments.js
// Rest parameter `...`
const moreConcat = (...arg) => {
  return arg.reduce((prev, curr) => {
    return prev + ' ' + curr;
  });
}
console.log(moreConcat('h', 'o', 'g', 'e', 'fuga!')); // -> 'h o g e fuga!'
```

### `this`がすぐ外を参照する
```js:that_this_trick.js
function myFunc() {
  this.myVar = 0;
  var self = this;
  setTimeout(function() { // Callbackで新しいthisが生成される
    self.myVar++;
    console.log(self.myVar); // -> 1
    console.log(this.myVar); // -> undefined
  }, 0);
}
```
```js:no_that_this.js
function myFunc() {
  this.myVar = 0;
  setTimeout(() => {
    this.myVar++;
    console.log(this.myVar); // -> 1
  }, 0);
}
```

## Function Default Parameter
```js:default_parameter.js
function getName(first = 'Michael', second = 'Jordan') {
  return first + ' ' + second;
}
console.log(getName('Keiichiro', 'Amemiya')); // -> 'Keiichiro Amemiya'
console.log(getName('Taro'); // -> 'Taro Jordan'
console.log(getName(undefined)); // -> 'Michael Jordan'
console.log(getName(null, 'Amemiya'); // -> 'null Amemiya'
```

## Destructuring （直訳： 構造分解）
### Object
```js
const person = {
  firstName: 'Nick',
  lastName: 'Anderson',
  age: '21',
  sex: 'M'
}

// ES5
var first = person.firstName;
var age = person.age;
var city = person.city || 'Paris';

// ES6
const { firstName: first, age, city = 'Paris'} = person;

console.log(first); // -> 'Nick'
console.log(age); // -> '21'
console.log(city); // -> 'Paris'
console.log(firstName) // -> undefined
```

#### 関数のパラメータにも使える
```js:parameter_destructuring.js
// ES5
function joinFirstLastName(person) {
  return person.firstName + '-' + person.lastName;
}

// ES6
function joinFirstLastName({firstName, lastName}) {
  return firstName + '-' + lastName;
}

const joinFirstLastName = ({firstName, lastName}) => `${firstName}-${lastName}`
```

### Array
```js
const myArray = [1, 2, 3];

// ES5
var x = myArray[0];
var y = myArray[1];
var z = myArray[2];

// ES6
const [x, y, z] = myArray;
```

### 応用編
```js
const [x, y, z] = myArray;
```
ここで`myArray`は変数でなくてもいい

#### 変数の入れ替え
```js
var x = 1;
var y = 2;

var temp = x;
x = y;
y = temp;
```
```js
let x = 1;
let y = 2;

[x, y] = [y, x];
```

#### 関数を宣言せずにまとめて処理
```js
const me = {
  name: 'Kei',
  age: 21,
  from: 'Yokohama'
};

const you = {
  name: 'hoge',
  age: 65,
  from: 'New York'
};
```
```js
function intro({name, age, from}) {
  return `My name is ${name}. I'm ${age} years old and from ${from}`;
}

const myIntro = intro(me);
const yourIntro = intro(you);

console.log(myIntro); // -> 'My name is Kei. I'm 21 years old and from Yokohama'
console.log(yourIntro); // -> 'My name is hoge. I'm 65 years old and from New York'
```
```js
const [myIntro, yourIntro] = [me, you].map(({name, age, from}) => `My name is ${name}. I'm ${age} years old and from ${from}`);

console.log(myIntro); // -> 'My name is Kei. I'm 21 years old and from Yokohama'
console.log(yourIntro); // -> 'My name is hoge. I'm 65 years old and from New York'
```

## `map`/`filter`/`reduce`
[Functional Programming](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0)の台頭により登場
#### 命令型 (Declarative Programming)
```js
const numbers = [0, 1, 2, 3, 4, 5, 6];

const doubledNumbers = [];
numbers.forEach(element => {
  doubledNumbers.push(element * 2);
});

const evenNumbers = [];
numbers.forEach(element => {
  if (element % 2 === 0) {
    evenNumbers.push(element);
  }
});

let sum = 0;
numbers.forEach(element => {
  sum += element;
});
```

#### 関数型 (Functional Programming)
```js
const numbers = [0, 1, 2, 3, 4, 5, 6];
const doubledNumbers = numbers.map(n => n * 2); // -> [0, 2, 4, 6, 8, 10, 12]
const evenNumbers = numbers.filter(n => n % 2 === 0); // -> [0, 2, 4, 6]
const sum = numbers.reduce((prev, next) => prev + next, 0); // -> 21
```
→やることを動詞で書くので読みやすい

#### 応用編
```js
const group = [
  {mobile: 'iPhone X', salary: 560},
  {mobile: 'iPhone 7', salary: 456},
  {mobile: 'iPhone 6S', salary: 340},
  {mobile: 'iPhone X', salary: 180},
  {mobile: 'iPhone 3GS', salary: 240},
  {mobile: 'Garake-', salary: 1000},
  {mobile: 'iPhone X', salary: 999},
  {mobile: 'iPhone X', salary: 7340},
  {mobile: 'iPhone 8', salary: 231},
  {mobile: 'iPhone 7 Plus', salary: 800},
  {mobile: 'iPhone X', salary: 450},
  {mobile: 'iPhone 5C', salary: 765}
]
```
iPhone Xを使っている人の年収の平均を出す

```js
// Declarative Programming
let sum = 0;
let counter = 0;
group.forEach(person => {
  if (person.mobile === 'iPhone X') {
    sum += person.salary;
    counter++;
  }
});
console.log(sum / counter);

// Functional Programming
const filtered = group
  .filter(person => person.mobile === 'iPhone X')
  .map(person => person.salary);
const average = filtered.reduce((prev, curr) => prev + curr) / filtered.length
console.log(average);
```
iPhone Xのみに絞って→給料のみの配列にして→平均を計算するという流れをそのまま書ける

## Spread Operator `...`
### 配列
```js
const arr1 = [1, 2, 3];

console.log([arr1, 4, 5, 6]); // -> [[1, 2, 3], 4, 5, 6]
console.log(arr1 + [4, 5, 6]); // -> '1,2,34,5,6'
console.log(arr1.concat([4, 5, 6])); // -> [1, 2, 3, 4, 5, 6]

console.log([...arr1, 4, 5, 6]) // -> [1, 2, 3, 4, 5, 6]
```
### 関数の引数
```js
function student() {
  const grades = [];
  for (let i = 2; i < arguments.length; i++) {
    grades.push(arguments[i]);
  }
  const avgGrade = grades.reduce((prev, curr) => prev + curr) / grades.length;
  return {
    name: arguments[0] + ' ' + arguments[1],
    grades: grades,
    average: avgGrade
  };
}

function student(firstName, lastName, ...grades) {
  const avgGrade = grades.reduce((prev, curr) => prev + curr) / grades.length;
  return {
    name: firstName + ' ' + lastName,
    grades: grades,
    average: avgGrade
  };
}
```

### Destructuringに使う
```js
const myArray = [1, 2, 3, 4, 5];
const [a, b, c, ...rest] = myArray;
console.log(rest); // -> [4, 5]
```
```js
const myObject = {
  name: 'Mike',
  age: 27,
  born: 'Yokohama',
  sex: 'M',
  degree: 'Math'
};
const {name, age, sex, ...otherInfo} = myObject;
console.log(otherInfo); // -> {born: 'Yokohama', degree: 'Math'}
```

## Object Property Shorthand
```js
const name = 'Roger';
const place = 'Tokyo';
const age = 34;
const university = 'MIT';
const job = 'engineer';

const roger = {name, place, age, university, job};
console.log(roger.name); // -> 'Roger'
console.log(roger.job); // -> 'engineer'
```

## Template Literals
```js
const msg1 = 'My name is' + name + 'and I\'m' + age + 'years old!';
const msg2 = `My name is ${name} and I'm ${age * 100} years old!`;

const msg3 = `hogehogehoge
fugafugafuga`;

const nested = `${person.sex === 'M' ? 'He' : 'She'} is ${person.name}${person.place ? `and from ${person.from ? person.from : person.place}` : ''}.`

const nested = `${person.sex === 'M' ? 'He' : 'She'} is ${person.name}${
  person.place
    ? `and from ${
      person.from
        ? person.from
        : person.place
    }`
    : ''
}.`
```

## Tagged Template Literals
関数にTemplate Literalをわたして、共通な処理を任せる

```js:replace_name.js
function myTag(strings, ...values) {
  return strings.reduce((prev, curr) => {
    return prev + curr + (values.length ? values.shift().name : '');
  }, '');
}

const me = {
  name: 'Kei',
  age: 21
};
const you = {
  name: 'Mike',
  age: 28
};
const him = {
  name: 'Hoge',
  age: 54
};

myTag`My name is ${me}, and your name is ${you}, and his name is ${him}. OK!`;
// -> 'My name is Kei, and your name is Mike, and his name is Hoge. OK!'
```
```js:handle_array.js
function myTag(strings, ...values) {
  return strings.reduce((prev, curr) => {
    let value = values.shift() || [];
    value = value.join(', ');
    return prev + curr + value;
  }, '');
}

const snacks = ['potechi', 'popcorn', 'candy'];

myTag`I like ${snacks}`;
/// -> 'I like potechi, popcorn, candy'
```

## `import`/`export`
```js:math_constants.js
export const pi = 3.14;
export const exp = 2.7;
export const alpha = 0.35;
```
```js:my_file.js
import {pi, exp} from './math_constants.js';
console.log(pi) // -> 3.14
```
```js:my_second_file.js
import * as constants from './math_constants.js';
console.log(constants.pi) // -> 3.14
console.log(constants.exp) // -> 2.7
```

### 別の名前にもできる
```js
import {foo as bar} from 'my_file.js';
```

### `default`
```js:class/my_class.js
export default class Hoge {
  // ...
}
```
```js:my_file.js
import Hoge from './class/my_class.js';
const hoge = new Hoge();
// ...
```

## Class構文
**クラスっぽい書き方をするだけで中身はプロトタイプ型のオブジェクト** (Syntactic Sugar)

```js:prototype.js
var Person = function(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.stringSentence = function() {
  return 'Hello, my name is ' + this.name + ' and I\'m ' + this.age;
}
// Static method
Person.whatIsThis = function() {
  return 'This is person!';
}
```
```js:class.js
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  stringSentence() {
    return `Hello, my name is ${this.name} and I'm ${this.age}`;
  }
  static whatIsThis() {
    return 'This is person!';
  }
}
```
→クラスを作っているわけではない。
このあとから`Object.prototype.hogehoge = `でメソッドを追加したりできる（できてしまう）。

### `extends`と`super()`
```js
class Student extends Person {
  constructor(name, age, school) {
    super(name, age);
    this.school = school;
  }
  get school() {
    return this.school + '高校';
  }
}
```

## ES8のObjectメソッド
- `Object.values`: valueの配列を返す
- `Object.entries`: keyの配列を返す
- `Object.getOwnPropertyDescritors`: そのオブジェクトのプロパティをいろいろ説明付きで返す

```js
const obj = { 
  get es7() { return 777; },
  get es8() { return 888; }
};
Object.getOwnPropertyDescriptors(obj);
// {
//   es7: {
//     configurable: true,
//     enumerable: true,
//     get: function es7(){}, //the getter function
//     set: undefined
//   },
//   es8: {
//     configurable: true,
//     enumerable: true,
//     get: function es8(){}, //the getter function
//     set: undefined
//   }
// }
```

## String Padding
上限の文字数を決めるとそこまで埋めてくれる

```js
'es8'.padStart(2);          // 'es8'
'es8'.padStart(5);          // '  es8'
'es8'.padStart(6, 'woof');  // 'wooes8'
'es8'.padStart(14, 'wow');  // 'wowwowwowwoes8'
'es8'.padStart(7, '0');     // '0000es8'
'es8'.padEnd(2);          // 'es8'
'es8'.padEnd(5);          // 'es8  '
'es8'.padEnd(6, 'woof');  // 'es8woo'
'es8'.padEnd(14, 'wow');  // 'es8wowwowwowwo'
'es8'.padEnd(7, '6');     // 'es86666'
```

## Promise, Async, Await
非同期処理でCallback地獄にならずにすむ書き方

### Promise
```js
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const name = prompt('Put your name!');
    if (name === 'Mike') {
      reject(name); // 失敗
    } else {
      resolve(name); // 成功
    }
  }, 1000);
});

promise.then((name) => {
  console.log('Hello, ' + name);
}).catch((name) => {
  console.error('You put ' + name + '!?');
});
```
Promiseコンストラクタは、成功時と失敗時に実行する関数を引数とする関数を1つ受け取る。
その後、`.then`に成功時の関数をわたし、`.catch`に失敗時の関数をわたす。

#### 処理をつなげる
```js
function myPromise() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (isSomething) {
        hoge();
        resolve();
      } else {
        reject();
      }
    }, 1000);
  });
}

myPromise().then(() => {
  console.log('Success');
  return myPromise();
}).then(() => {
  console.log('Success');
  return myPromise();
}).catch(() => {
  console.warn('Error');
})
```

#### `Promise.all(iterable)`
複数の非同期処理が全部終わったら次の処理に行く。

#### `Promise.race(iterable)`
複数の非同期処理のうち最初に終わったものの結果を次の処理に行く。

### Async/Await関数
`async`をつけた関数の中で、非同期処理の前に`await`を書く。

```js
async function myAsyncFunc() {
  const response = await fetch('https://...');
  // ...
}
myAsyncFunc().then(() => {
  // 成功時処理
}).catch((err) => {
  throw err;
})
```

# ES9
##リリースが確定したProposal
- [Lifting template literal restriction](https://tc39.github.io/proposal-template-literal-revision/)

##[Stage 3のProposal](https://github.com/tc39/proposals)
- `Function.prototype.toString` revision
- `global`
- Rest/Spread Properties
- [Asynchoronous Iteration](http://2ality.com/2016/10/asynchronous-iteration.html)
- `import()`
- RegExp Lookbehind Assertions
- RegExp Unicode Property Escapes
- RegExp named capture groups
- `s` (`dotAll`) flag for regular expressions
- Legacy RegExp features in JavaScript
- `Promise.prototype.finally`
- BigInt
- Class Fields
- Optional catch binding
- `import.meta`