JavaScript
es6
babel
es2015

ES6(ES2015)チートシート

素のままのJavaScript(ES5)に直接触れる機会もだんだんと減ってきたので、うろ覚えだったES6の機能をまとめてみました。

なお文中のコードはBabel環境で書きながら確認したため、現行ブラウザでの直接の対応状況に関しては未調査です。


概要

ECMAScriptS6 = ES2015 = JavaScriptの新しいバージョン


  • ES5(現行のjs)の次のバージョンとして策定された

  • さらに新しいES7も作られている

現状は多くのブラウザが(部分的にしか)対応していないので、ES6で書いたコードを動かすためにはES5への変換(トランスパイル)が必要になる。

トランスパイル用のツールとしてはBabelが代表的な実装になる。

具体的な利用方法としては:



  • ES6をサポートしているブラウザを使う


    • 現状、サポートは部分的かつ挙動に差異がある



  • babelのCLIを使って直接トランスパイルする


  • webpack, browserify, sprocketsなどがバンドルを作成する過程にBabelを組み込む


  • Bebel/browser.jsを使って表示のたびにトランスパイルする(デバッグ用)


など。

導入方法は以前React-Redux環境構築の際に調査済なので、今回は省略する。

(React-Redux on Rails(w/webpack/yarn)環境を作ったメモ)


構文の追加/変更


モジュール(import/export)

CommonJSなどサードパーティレベルで実現していたファイル分割/依存関係解決の機能が標準機能となった。

あるモジュール(ファイル)からexportしたクラス・変数・定数・関数を、他のモジュールがimportするかたちで使う。

import/exportの指定方法は復数あるので詳細はMDNなどを参照のこと。


exportする例


// 変数・定数・関数を、名前を指定して直接出力する
export let someVariable = ...
export const SOME_CONST = ...
export const someFunc = () => {
...
}

// デフォルト(ファイル=モジュールにひとつ)として出力する
export default class SomeClass{
...
}



importする例


// モジュール内の全てをSomeModuleにバインドする
import * as SomeModule from 'path/to/module'

// デフォルトを名前空間SomeModuleにバインドする
import SomeModule from 'path/to/module'

// モジュールをバインドせずにロードだけする
import 'path/to/module'

// モジュール内のfooとbarだけロードする
import {foo, bar} from 'path/to/module'



変数と定数(let/const)

varの代替として、letconstを変数の宣言時に使える。

constで宣言すると定数となり、変更できない。

(Babel環境の場合はトランスパイル時に静的なエラーとなる

letの機能はvarと同じ。

constの対比として、変更できることを明示する意味合いで使う。

let foo = 'foo'

const bar = 'bar'

// OK
foo = 'hoge'

// Error
bar = 'piyo'


クラス(class/extends/super/static)

クラス定義を構文レベルでサポートする。

(シンタックスシュガーにすぎないので、内部的には従来のプロトタイプベース構造から変わっていないとのこと)

class Foo extends SomeSuperClass{

// コンストラクタ
constructor(bar, baz){

// 親クラスのメソッドが呼べる
super()

// インスタンス変数はコンストラクタ内で定義する(これまでと同じ)
this._bar = baz
this._baz = baz
}

// インスタンスメソッド
bar(){
return this._bar
}

// クラスメソッド
static foo(){
return 'FOO'
}
}

let foo = new Foo('BAR', 'BAZ')
console.error(foo.bar())
console.error(Foo.foo())


オブジェクト定義の簡略化

オブジェクト定義の構文が強化された。

let magicKey = 'foo'

let obj = {
// プロトタイプを指定する
__proto__: theProtoObj,

// キー名を省略できる(foo: foo, と同義)
foo,

// メソッドを簡単に書ける
someMethod(){
// クラスと同様にsuper()を呼べる
super()
},

// 動的なキーを使う(obj.foo)
[ magicKey ]: 'magic value'
}


アロー関数(=>)

関数定義の省略記法として()=>が使える。

アロー関数中のthisは、その外側のスコープのthisに等しい(いわゆるselfを定義しなくていい)

// 基本形

let allowFunc = () => {
// ...
}

// 外側のthisを参照できる
var someObj = {
_someParam: '...',

getSomeParam(){
return () => {
this._someParam // ここでのthisはsomeObj
}
},
}

// 単行の場合は省略可能
let simpleFunc = () => otherFunc()

// 引数がひとつの場合は括弧も省略可能
let funcWithArg = arg => otherFunc(arg)


テンプレート文字列(${})

文字列中に変数を埋め込んで、その内容を展開できる。

テンプレート文字列は\`で囲む。

(ちなみにmarkdownでよく使うこの文字は、グレイヴ・アクセントというらしい)

const foo = 'foo'

const bar = 'bar'
const baz = () => {
return 'baz'
}

// 変数を展開する
const foobar = `${foo}, ${bar}`

// 式の評価もできる
const some = `${baz()}`


2進/8進リテラル(0b/0o)

2進数、8進数をリテラルとして表現できるようになった。

let bin = 0b1001    // 2進

let oct = 0o11 // 8進
let dec = 9 // 10進
let hex = 0x09 // 16進


デフォルト / 可変長引数(=/...)

引数にデフォルト値を設定できるようになった。

また可変長にも対応した。

// デフォルトをとる

function add(x, y=0){
return x + y
}
add(3)
add(4, 5)

// 可変長引数をとる
function sum(...args){
return args.reduce(function(count, val){
return count + val
}, 0)
}
sum(1, 2, 3)


コレクションの繰り返し(for-of)

繰り返しの制御構文としてfor-of文が追加された。

既存のfor-in との違いは、オブジェクト以外のコレクション(Array, Map, Set, Iterator, String)にも対応していること。

また後述のイテレータを回すこともできる。

// 配列を回す(for-in): for-inなので"インデックス"が"文字列"で返る('0', '1', '2')

for(let n in [10, 20, 30])
console.log(n)

// 配列を回す(for-of): 整数型の値10, 20, 30が返る
for(let n of [10, 20, 30])
console.log(n)

// 文字列も回せる
for(let n of 'abc')
console.log(n)


分割代入

英語ではDestructuring(非構造化)とのこと。

配列やオブジェクトの内容を、復数の変数に(分割して)代入する。

// 配列を分割する

const [foo, bar] = ['foo', 'bar']
console.log(foo)
console.log(bar)

// オブジェクトを分割する(大文字がオブジェクトのキー、小文字が代入される変数)
const {FOO: hoge, BAR: piyo} = {'FOO' : 'foo', 'BAR' : 'bar' }
console.log(hoge)
console.log(piyo)


配列展開

...で配列を展開し、引数や配列の初期化処理内で利用できる。

let list = [3, 4, 5]

// 展開して結合する
list = [1, 2, ...list, 6, 7]
console.log(list)

// 展開して可変長引数に渡す
print(...list)

function print(...args){
console.log(args)
}


型とオブジェクトの追加/変更


基本型の継承(Array, Date, Element)

ES5では継承できなかった(推奨されていなかった) Array, Date, Elementが継承可能になった。

class CustomArray extends Array{

// ...
}


シンボル型(Symbol)

シンボルは、文字列に似た一意なオブジェクト。



  • プリミティブ型である

  • それ自身とのみ等価(==)、等値(===)となる

let secretKey = Symbol('foo')

let anotherKey = Symbol('foo')

// 同じ文字列で初期化しても、シンボルは別オブジェクトとして認識される
console.log(secretKey == anotherKey) // false
console.log(secretKey === anotherKey) // false

// 文字と比較しても同様
console.log(secretKey == 'foo') // false
console.log(secretKey === 'foo') // false

// 自身とだけ合致する
console.log(secretKey == secretKey) // true
console.log(secretKey === secretKey) // true

rubyの:symbolのようなリテラル表記は特にない様子だった。


コレクション型(Map/Set/WeakMap/WeakSet)


  • Set = 重複のない配列



  • Map = "連想配列"に近いもの


    • 既存のObject({ })より格納の自由度が高い

    • かつ、操作用のメソッドが色々ある




  • WeakSet / WeakMap = 弱参照のSet/Map


    • 参照(オブジェクト)のみ保持できる

    • 格納しても、オブジェクトを保持しない(弱参照) = GCされうる




Set

let set = new Set()

set.add('foo')
set.add('foo') // 値の重複はエラーとはならないが、数にはカウントされない
set.add('bar').add('bar') // メソッドチェーンできる

console.log(set.size) // 2
console.log(set.has('foo')) // true



Map

let map = new Map()

let strKey = 'key'
let listKey = [0, 1, 2]

map.set(strKey, 'string value')
map.set(listKey, 'list value') // 配列オブジェクトをキーにすることができる

console.log(map.get(strKey)) // 'string value'
console.log(map.get(listKey)) // 'list value'



プロミスパターン(Promise)

Promiseパターン実装のためのオブジェクト。

非同期処理を先に実行し、その後からでも結果のコールバックが設定できる。

なのでこれを使うと、


  • 処理の順序とコードの記述順序が同じになる

  • 非同期処理が連続する場合に、コード上の深いネストを避けられる

const promise = new Promise((resolve, reject) => {

// ...何か非同期的な処理を行う

// 成功したら呼ぶ
resolve()
// 失敗したら呼ぶ
reject()
})

promise.then(() => {
console.log('success');
})

promise.catch(err => {
console.error('failed: ' + err)
})


プロキシパターン(Proxy)

Proxyパターン実装のためのオブジェクト。

もとのオブジェクトをラップしてふるまいを追加/変更する。

// Proxyにつつまれるオブジェクト

var data = {};

// Proxyの処理内容
var handler = {
get: function(obj, name){

// ...

return obj[name]
},

set: function(obj, name, value){

// ...

obj[name] = value
return true
},
};

// Proxyオブジェクトを作って操作
var proxy = new Proxy(data, handler)
proxy.foo = 'FOO'
proxy.bar = 'BAR'
console.log(proxy.foo)
console.log(proxy.bar)

ハンドルできるイベントはget, set, construct, deletePropertyなど色々。


イテレータ(Symbol.iterator)

任意のオブジェクトのSymbol.iteratorプロパティにイテレータを定義することで、for-ofによってループを回すことができる。

let list = {}

// イテレータを定義する
list[Symbol.iterator] = function(){
let i = 0
let iter = {
next: () => {
return { done: false, value: i++}
}
}

return iter
}

// forで取得する
let iter = list[Symbol.iterator]()
for(let i=0; i < 10; i++){
let result = iter.next()
if(result.done) break
console.log(result.value)
}

// for-ofで取得
for(let n of list){
if(n >= 10) break
console.log(n)
}


ジェネレータ

function*でPython方式のジェネレータを定義できる。

なおBabelでジェネレータ構文を使う場合はbabel-polyfillパッケージが必要だった。

require('babel-polyfill')

let list = {}

// ジェネレータを定義する
const gen = function*(){
for(let i=0;;)
yield i++
}

// for-ofで生成する
for(let n of gen()){
if(n >= 10) break
console.log(n)
}


参考