Javascriptは分かるけど、ES6(ES2015)はイマイチ分からない…ということで、ES6で新たに追加された機能や構文を学ぶために参考になった記事をまとめてみました。

develop or drink ?
 develop or drink ? ©

当記事ではBabelの「Learn ES2015」のトピックに合わせて和訳したものをまとめてみたいと思います。

目次

Babelとはなにか

Babelは、ブラウザにまだサポートされていないようなJavaScriptの次世代の標準機能を、現在のブラウザでも使えるようにする機能(=トランスパイラ)をNode.jsで使えるようにしたもの。BabelのホームページではJavascriptの次世代標準機能を「Learn ES2015」で紹介しています。

また、ES6を学ぶには下記の書籍も参考になります。

ES6と各ブラウザや各言語の対応状況

各ブラウザやNode.jsなどの各言語への対応状況はECMAScript 6 compatibility tableで随時確認することができます。

Arrows and Lexical This

関数をアロー演算子(=>)で書けるようになりました。

arrow.js
1
2
3
4
5
6
7
8
9
10
11
12
13
// アロー演算子で関数を書ける
let exampleFunction = (v1 , v2) => {
return v1 + v2
}

// 一行で済む場合はreturnを省略できる
// let exampleFunction = (v1 , v2) => v1 + v2

// さらに引数が1つの関数は括弧を省略できる
let exampleFunction2 = v => v + 1

console.log(exampleFunction(1 ,2)) // 3
console.log(exampleFunction2(1)) // 2

class

クラスの定義とクラス定義ができるようになりました。

class.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Hoge{
constructor(fuga){
this.fuga = fuga;
}
piyo(){
alert(this.fuga);
}
}

var hoge = new Hoge('FUGA');
hoge.piyo();

class HogeSub extends Hoge{
constructor(fugaSub){
super(fugaSub);
}
piyoSub(){
super.piyo('classの継承');
}
}
var hogeSub = new HogeSub('できました!');
hogeSub.piyoSub();

LIG
ES6の新機能「class構文」 – 基礎編
ES6のClassについて分かりやすく解説されている

Enhanced Object Literals

オブジェクトのキーを動的に定義することができるようになりました。キーは従来の文字列以外に、変数などで指定できます。

enhancedObjectLiterals.js
1
2
3
4
5
6
7
8
9
let arrName = "test"

let ob = {
arrName : 1 ,
[arrName] : 2
}

console.log(ob.arrName)
console.log(ob.test)

れおの備忘録
Babel: ES6で動的なオブジェクトプロパティをリテラルで定義する方法

Template Strings

文字列の扱いが従来よりも便利になりました。

string.js
1
2
3
4
5
6
7
8
9
console.log(`文字列`.length)

// 改行も有効になる
console.log(`文字列
改行もできる`.length)

// テンプレート機能
let s1 = "1文字目", s2 = "2文字目";
console.log(`文字列に${s1}${s2}を埋め込む`)

Destructuring

分割代入ができるようになりました。

destructuring.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 分割代入
let [a, ,b] = [1,2,3]
console.log(a === 1) // true
console.log(b === 3) // true

let { op: a, lhs: { op: b }, rhs: c } = getASTNode()
// イメージとしては以下のような形で変数に代入される
// a = _getASTNode.op,
// b = _getASTNode.lhs.op,
// c = _getASTNode.rhs;

let {op, lhs, rhs} = getASTNode()
// イメージとしては以下のような形で変数に代入される
// op = _getASTNode2.op,
// lhs = _getASTNode2.lhs,
// rhs = _getASTNode2.rhs;

// オブジェクトキーを引数にもつ関数
let g = ({test: x}) => console.log(x)
g({test: 5})

// 変数の破棄
let [a] = []
console.log(a === undefined) // true

// 変数の破棄と初期化
let [a = 1] = []
console.log(a === 1) // true

// 関数のデフォルト引数の指定ができる
let r = ({x, y, w = 10, h = 10}) => x + y + w + h
console.log( r({x:1, y:2}) === 23 )

Default + Rest + Spread

関数の引数の定義方法が増えました。

defaultRestSpread.js
1
2
3
4
5
6
7
8
9
10
11
// デフォルトの引数
let fn1 = (x, y=12) => x + y
console.log( fn1(3) == 15 )

// yは["hello", true]
let fn2 = (x, ...y) => x * y.length
console.log( fn2(3, "hello", true) == 6 )

// 配列の引数
let fn3 = (x, y, z) => x + y + z
console.log( fn3(...[1,2,3]) == 6 )

Let + Const

新たに変数宣言としてletconstが使えるようになりました。

analogic
var, let, constの使い分けについて

Iterators + For..Of

配列などの要素を、従来よりも簡単に取り出せるようになりました。

Iterator(イテレータ)とは、配列などのコレクションの要素を、一つずつ順番に取り出すことのできる性質をもったもののことです。また取り出せる性質を持っているオブジェクトをイテラブルなオブジェクトと呼んでいます。

ES6ではイテレータ、イテラブルという用語が度々登場してくるので、しっかり覚えて置いたほうが良さそうです。

Qiita
JavaScript の イテレータ を極める!
イテレータとは何か?またその使い方を分かりやすく噛み砕いて解説している。またFor Ofや配列や文字列がイテラブルなオブジェクトである事が触れられているなど、理解を深めやすい内容となっている。

Iterators.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let arr = [ 1 ,2 , 3]
for(let elm of arr){
console.log(elm)
}

// 結果
// 1
// 2
// 3


let arr2 = ["A", "B", "C"]; // 配列はイテラブルなオブジェクト
for(var v of obj.keys()) console.log(v);

// 結果
// 0
// 1
// 2

for(var v of arr2.entries()) console.log(v);

// 結果
// [0, "A"]
// [1, "B"]
// [2, "C"]

ES6では、イテレータの処理内容を自由に定義する事もできます。以下は偶数を返すイテレータの例です。

Iterators2.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// イテレータを定義
let fibonacci = {
[Symbol.iterator]() {
let pre = 0;
return {
next() {
pre = pre + 2;
return { done: false, value: pre }
}
}
}
}

for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 10)
break;
console.log(n);
}

Generators

イテレータの一種であり、イテレータをより強化したようなものです。1つの要素を取り出そうとするたびに処理を行い、要素を生成してくれる関数を定義します。

Qiita
JavaScript の ジェネレータ を極める!
ジェネレータとイテレータの違いが分かりやすく解説されている。またジェネレータの使い所や、yieldの使い方がサンプルコードで分かりやすく説明されている。

generator.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1〜10を取り出すfor-of文
var ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for(var num of ary) console.log(num);

// 上記をジェネレータで書くと下記のようになる。
// ジェネレータ関数で生成方法を定義できるので、配列の要素を書かなくて良くなる。
// 下記はQiitaで解説されいるコードの引用です。
// ジェネレータ関数
function* gfn(from, to){ while(from <= to) yield from++; }
// ジェネレータ
var g = gfn(1, 10);
for(var num of g) console.log(num);

// 結果
// 1
// 2
// 3
// ...
// 10

yieldとは?

ジェネレータで使われるyieldは、記述した位置で実行を一旦停止して、その結果を返すものとなっています。

generator-yield.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// ジェネレータ関数
// yieldは、yeildの記述されている位置で、一度実行を停止して結果を返す
function* gfn(n){
n++;
yield n;
n += 2;
yield n;
n = 0;
yield n;
}

var g = gfn(0); // ジェネレータ
console.log( g.next() ); // 結果 {"done": false,"value": 1}
console.log( g.next() ); // 結果 {"done": false,"value": 3}
console.log( g.next() ); // 結果 {"done": false,"value": 0}
console.log( g.next() ); // 結果 {"done": true,"value": undefined}


// yield*はイテラブルなオブジェクト(例えば配列や文字列)から要素を順番に取り出し
// その値ごとにyieldを行うものとなっている
function* gfn2(){
yield* [1, 3, 5];
}

var g2 = gfn2();

console.log( g2.next() ); // 結果 { value: 1, done: false }
console.log( g2.next() ); // 結果 { value: 3, done: false }
console.log( g2.next() ); // 結果 { value: 5, done: false }
console.log( g2.next() ); // 結果 { value: undefined, done: true }

Unicode

Unicodeリテラルが追加されました。サロゲートペア対象の文字列でも、従来のように分割せずに表記できるようになります。

Qiita
ES2015に追加、拡張された機能

サロゲートペアについては以下の記事が参考になります。

CodeZine
サロゲートペア入門
近年、Unicodeに組み込みたいという文字の要望がいろいろと増えてきました。結果的に従来の2バイト(65536文字)では文字が足りない状況になってしまったのです。そこで、解決策としてサロゲートペアという方法が導入されました。これは、「1文字=2バイト」の基本は維持しつつ、一部の文字については「1文字=4バイト」にする方法です。

unicode.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// same as ES5.1
console.log("𠮷".length == 2)

// new RegExp behaviour, opt-in ‘u’
console.log("𠮷".match(/./u)[0].length == 2)

// new form
console.log("𠮷" == "\uD842\uDFB7") // サロゲートペアの表現(従来)
console.log("\u{20BB7}" == "𠮷") // Unicodeリテラル

// new String ops
console.log("𠮷".codePointAt(0) == 0x20BB7)

// for-of iterates code points
for(var c of "𠮷") {
console.log(c);
}

Modules

export文やimportを使用することで、関数やオブジェクトをエクスポートやインポートすることができます。

Think IT
ES2015のモジュール管理
モジュールとは何かという内容から始まり、モジュール管理の必要性を解説している

lib/math.js
1
2
3
4
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;

上記のようにモジュールを定義しておき、下記のようにインポートします。

app.js
1
2
3
4
5
6
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));

// また、下記のように読み込む事もできる
// import {sum, pi} from "lib/math";
// console.log("2π = " + sum(pi, pi));

default export

また、モジュール1つに対して1つのメンバーだけ、名前を指定せずにエクスポートすることができます。

lib/math2.js
1
2
3
export default function(x) {
return Math.exp(x);
}
app.js
1
2
import exp from "lib/math2";
console.log("e^π = " + exp(pi));

Map + Set

Mapはキーに対して値を保存しておけるものです。オブジェクト(Object)と似ていますが、キーは文字列以外にも数値やオブジェクトを指定することができます。更に、

  • 値の取得
  • 値の存在確認
  • データの削除
  • キーの取得や値の取得

など、オブジェクトでよく行う処理を、シンプルに行えるようにメソッドが用意されているのも良い点です。

YoheiM.NET
JavaScript ECMAScript6のMapとSetを使ってみよう
Mapの使い方がシンプルに解説されていて理解しやすい

Setとは?

SetはMapと同様のメソッドが用意されています。

Mapと異なる点は、Setは配列を扱うものとなっている点です。また値の重複が許されないという性質を持っています

Setで値を追加する際には、重複を気にせず値を追加する事ができる(重複した場合には値が新たに追加されない)ので、一意な配列データを扱いたい時に活用したい機能です。

WeakMap + WeakSet

WeakMapはMapと違い、オブジェクトの参照のみをキーに指定できます。また、キーに指定したオブジェクトの参照を開放する(例:obj = null)とすると、WeakMapに格納したデータも消滅するという性質があります。

uhyohyo.net
Map クラスについて
「WeakMap とオブジェクトキーの参照関係について」の項目でWeakMapの使い方を分かりやすく解説している

javascript プログラミング講座
WeakSetとWeakMap
WeakMapとは何かについて、とても分かりやすい解説をまとめている。
(引用)keyとなるオブジェクトを汚さないというのがWeakMapの1つの利点。オブジェクトにプロパティとして勝手に付加する方式だと、外部からそれを書き換えられてしまう可能性もあります。WeakMapならば、それを持っている自分しか値を読んだり書き換えたりすることができず安全。

Proxies (proxy) + Reflect API

Proxyはオブジェクトの持っている機能(トラップ関数)に割り込み、独自に定義した命令を実行させることができます。

Qiita
【JavaScript】Proxyオブジェクトなるものを学ぶ

トラップ関数(補足タイプ)と、使い方については以下の記事が参考になります。

JS.next
Proxyについて

ReflectはProxyと同じメソッドを持ち、静的な関数を返します。上記の記事で解説されているように、ReflectはProxyの内部で、オブジェクトの本来の動作を再現する場合に使われる事が多いようです。

Symbols

既存のプログラムやメソッドに影響を与えないように、独自のメソッドを安全に作ったり拡張する事ができます。

JS.next
ECMAScript6にシンボルができた理由

Subclassable Built-ins

ArrayやDate、DOM Elementsなど、ビルトインクラスを継承し、サブクラスを作成することができるようになりました。

Think IT
ES2015に追加、拡張された機能 Subclassable Built-ins

Math + Number + String + Object APIs

Javascriptで今まで備わっていた標準ライブラリに、いくつか新機能が追加されました。

  • Math

    • 常用対数
    • 双曲線関数、逆双曲線関数
    • 平方根、立方根、少数から整数、正負の符号
    • ビット計算
  • Number

    • イプシロン(ε)
    • 有限数、整数、NaNの判定
    • 文字列から浮動小数点数に変換
    • 基数を元にした整数に変換
  • String

    • 特定の文字列が存在するか判定
    • 文字列の指定回数分の生成
  • Object

    • オブジェクトのコピー
    • オブジェクトのマージ
  • Array

    • Array型への変換(Array.from)
    • 引数から新しいArrayを作成(Array.of)
    • Arrayの指定範囲のコピー(Array.copyWithin)

      第2引数から第3引数までの範囲をコピーし、第1引数で指定した開始位置にコピーする

    • Arrayからkeyとvalueの組み合わせを取得(Array.entries)

Think IT
既存の標準ライブラリ(Math/Number/String/Object/Array)に追加された新機能

Binary and Octal Literals

2進数(Binary数値)と8進数(Octal数値構文)が新たにサポートされました。それぞれ0〜10〜7までの数値を使うことになりますが、その範囲を超えた場合にはシンタックスエラーが出るようになっています。

MDN javascript - 字句文法
Numericリテラル

Promises

非同期処理を従来よりも単純で見通しの良いコードで記述できます。

Think IT
ES2015が備えるモダンな非同期処理
Promiseの使い方や、従来の記述方法と比較して、Promiseの良点を解説している。また新たに追加されたAsync/awaitについても単純なサンプルコードで分かりやすく解説されている。

Async / awaitについてはnode.js 7.6以降からサポートされており、まだ対応状況は少なそうです。しかし、Promiseよりも更にシンプルに非同期処理を記述できるため、今後は活用されるシーンが増えそうです。

InfoQ
Node 7.6、async/awaitをデフォルトでサポート

Reflect API

Proxies (proxy) + Reflect APIの項目を参照。

Tail Calls

再帰関数で関数呼び出しの階数が深くなりすぎると、発生することのあるスタックオーバーフロー。これを解消する方法として末尾再帰(Tail Calls)変換があります。

ブラウザでは殆ど対応していないのが現状のようで、現在はBabelがトランスパイル(Javascriptに変換)してくれる場合に限り使えるようです。ES2015では再帰関数を末尾呼び出し最適化してくれます。

Qiita
末尾再帰による最適化
Babelの末尾呼び出し最適化について解説されている。また付録として多重再帰関数や相互再帰関数についても紹介されている

ということで

ES6で新たに追加された機能や構文を学ぶために参考になった記事をまとめてみました。ES6はJavascriptとは大分違っており、出来ることがかなり増えました。

まとめた私も、まだ全てをマスターしきれていない状況なので、こちらの記事を振り返りながら学んでいきたいと思います。また、ES6を学ぶには下記の書籍も参考になります。こちらもご覧ください。