今更ながら速攻でES2015(ES6)になれる
最近フロントエンド開発からちょっと離れている間に、React.JSやAngular2、ES2015等々、変化の早いJS各界にちょっとでも遅れを取り戻せれたらと、思う今日このごろですw
まずはES2015(ES6)からやっていこうと思います
ES6について
公式発表は2015年6月で、既に現時点でES2016がリリースされているようです。
主な仕様
- class定義が可能
- import/export命令によるコードのモジュール化に対応
- 関数構文の改善(引数のデフォルト値、 可変長引数、 アロー関数など)
- let/const命令によるブロックスコープの導入
- for...of命令による値の列挙
- イテレーター/ジェネレーターによる列挙可能なオブジェクトの操作
- Promise、 コレクション(Map/Set)、 Proxyなどの組み込みオブジェクトを追加
- String, Number, Array, Objectなどの既存組み込みオブジェクトの拡張など
トランスコンパイラ
ES6をES5のコードに変換するツール
- Babelが一番主流っぽい
Polyfilライブラリ
ES6を実行時にES5に変換して実行するライブラリ
- babel-es6-polyfill
- es6-shim
let
ブロックスコープを有効にする
- ES5
if (true) { var num = 1; } console.log(num); // 1
- ES6
if (true) { let num = 1; console.log(num); let num = 2; // 重複定義でエラー console.log(num); } console.log(num); // スコープ外なのでエラー
const
定数を宣言する
const ITEM_FLAG = ture; ITEM_FLAG = false; // エラー
2進数/8進数リテラル
let num = 10; console.log(num.toString(8)); // "12" console.log(num.toString(2)); // "1010" console.log(0o10); // 8 console.log(0b11); // 3 console.log(Number('0o12')); // 10 console.log(Number('0b1010')); // 10
テンプレート文字列(Template Strings)
バッククォートで括ることで、改行文字をそのまま文字列の中で表現できたり、変数展開出来たりする
console.log(`Hello world. Oh yeah!!!`); // "Hello world.\nOh yeah!!!" let name = 'Bob'; console.log(`Hello ${name}!!`); // "Hello Bob!!"
タグ付きテンプレート文字列(Tagged template strings)
テンプレート文字列を関数に渡して、テンプレート文字列による出力をカスタマイズすることが出来る
let a = 5; let b = 10; function tag(strings, ...values) { console.log(strings[0]); // "Hello " console.log(strings[1]); // " world" console.log(values[0]); // 15 console.log(values[1]); // 50 return "Bazinga!"; } tag`Hello ${ a + b } world ${ a * b}`;
関数名
テンプレート文字列
で関数に引き渡す
第1引数でテンプレート文字列(分解したもの)、第2引数(可変長引数)で文字列に埋め込まれた値のセットを受け取る
Symbol
ユニークで不変なデータ型で、オブジェクトのプロパティ識別子として使われたりする
let hoge = Symbol('hoge'); let hoge2 = Symbol('hoge'); console.log(typeof hoge); // "symbol" console.log(hoge.toString()); // "Symbol(hoge)" console.log(hoge === hoge2); // false
利用例
- 定数の値として利用する
非公開なプロパティを定義する
1の例
// 識別するための定数 var AMAZON = 0; var GOOGLE = 1; var APPLE = 2; // 仮に'AMAZON'でないもので0が渡ってきた場合にバグになる if (site === AMAZON) { ... } // 以下ユニークにSymbolを生成するので意図しない動きにならなくなる const AMAZON = Symbol(); const GOOGLE = Symbol(); const APPLE = Symbol();
分割代入
配列やオブジェクトを分割し、その要素を個々の変数に展開するための構文
let [hoge, fuga, ...foo] = [1, 2, 3, 4, 5]; console.log(hoge); // 1 console.log(fuga); // 2 console.log(foo); // [3, 4, 5]
展開演算子
関数を呼び出す際に、列挙可能なオブジェクトを個々の変数に展開するための演算子
...[配列]
ES6以前ではFunction.prototype.apply
が使用されていた
function myFunction(x, y, z) { } var args = [0, 1, 2]; myFunction.apply(null, args);
これが↓のようになる
function myFunction(x, y, z) { } var args = [0, 1, 2]; myFunction(...args);
for..of命令
列挙可能なオブジェクトからすべての要素を取り出せる
for (variable of iterable) { statement }
Array
let iterable = [10, 20, 30]; for (let value of iterable) { console.log(value); } // 10 // 20 // 30
Map
for (let [key, value] of iterable) { console.log(value); } // 1 // 2 // 3
String
let iterable = "boo"; for (let value of iterable) { console.log(value); } // "b" // "o" // "o"
for-inとの違い
for...in
はオブジェクトのすべての enumerable なプロパティを反復するのに対し、
for..of
は値のみを列挙する
デフォルト引数
ES6からデフォルト引数が使用可能になった
function show(args = 'val') { console.log(args); }
可変長引数
仮引数の前に...
を付与することで、可変長引数となる
function hoge(...args) { }
アロー関数
引数 =>
本体の形式で関数を表現できる
returnを明記しなくてもよくなる
(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression // これと等価: => { return expression; } // 引数を 1 個しか取らない場合、丸括弧 () は任意です: (singleParam) => { statements } singleParam => { statements } // 引数を取らない場合、丸括弧が必要です: () => { statements } // object リテラル式を返す場合は、本体を丸括弧 () で囲みます: params => ({foo: bar}) // 残りの引数 と デフォルト引数 がサポートされます (param1, param2, ...rest) => { statements } (param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements } // 引数リスト内の 分割代入 もサポートされます var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6
thisが固定化
function Person(){ this.age = 0; setInterval(() => { this.age++; // |this| は person オブジェクトを適切に参照します。 }, 1000); } var p = new Person();
Promiseオブジェクト
非同期処理をあたかも同期処理であるかのように扱える
new Promise(executor); new Promise(function(resolve, reject) { ... }); // resolve : 処理の成功時の関数を渡す // reject : 処理の失敗時の関数を渡す
Promise.prototype.then(onFulfilled, onRejected)
thenメソッドでPromiseオブジェクトの処理結果を受け取ることが出来る
↓こちらがとても詳しくまとめられています
Promise.all
複数の非同期処理を並列に実行し、その全てが成功した場合に、処理を実行
Proxyオブジェクト
プロパティの設定、取得、削除、for...of/ for...inによる列挙など、オブジェクトの標準的な操作を、アプリ独自の操作で差し替えるためのオブジェクトです。
var p = new Proxy(target, handler);
- target : ターゲットのオブジェクト (ネイティブの配列、関数、あるいは他の Proxy も含め、どのような種類のオブジェクトでもかまいません) または、Proxy でラップする関数。
- handler : 関数をプロパティとして持つオブジェクトで、その関数で、Proxy に対して操作が行われた場合の挙動を定義します。
var handler = { get: function(target, name){ return name in target? target[name] : 37; } }; var p = new Proxy({}, handler); p.a = 1; p.b = undefined; console.log(p.a, p.b); // 1, undefined console.log('c' in p, p.c); // false, 37
コレクション関連のオブジェクト
Map
Javaでいうところの、HashMap
, PHPでの連想配列、RubyでのHash
、Pythonでのdict
key: valueのセットを管理
let user = new Map(); user.set('name', '太郎'); user.set('sex', 'gender'); user.set('age', 34); console.log(user.get('name')); // 太郎
Set
重複を取り沿いた値の集合を管理
let names = new Set(); names.add('太郎'); names.add('次郎'); names.add('太郎'); // 重複なので追加されない
Unicode対応の改善
サロゲートペア文字を正しく1文字として扱えるようになった
クラス定義/メソッド定義
ES6でclass命令が導入された まだ多少改善の余地はあるものの、他のオブジェクト指向言語での設計手法でやってた人に易しくなったかなと
- ES6以前でのメソッド記法
var obj = { foo: function() {}, bar: function() {} };
↓ES6では短縮できる
var obj = { foo() {}, bar() {} };
- class
クラス宣言、もしくはクラス式で定義されたクラス本体は、strict モード で実行される
class Polygon { // 初期化時に呼び出される constructor(height, width) { this.height = height; this.width = width; } }
プロトタイプメソッド
get/setが利用できる
class Polygon { constructor(height, width) { this.height = height; this.width = width; } get area() { return this.calcArea(); } calcArea() { return this.height * this.width; } } const square = new Polygon(10, 10); console.log(square.area);
static修飾子
静的メソッドを定義できる(インスタンスを生成せずに呼び出し可能)
class Point { constructor(x, y) { this.x = x; this.y = y; } static distance(a, b) { const dx = a.x - b.x; const dy = a.y - b.y; return Math.sqrt(dx*dx + dy*dy); } } const p1 = new Point(5, 5); const p2 = new Point(10, 10); console.log(Point.distance(p1, p2));
継承
extends
キーワードを使用することで、classの継承が可能
class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + ' makes a noise.'); } } class Dog extends Animal { speak() { console.log(this.name + ' barks.'); } }
モジュール
importキーワードで呼び出しファイルを指定できるようになった
// "my-module.js"ファイルがimportされる import * as myModule from "my-module";
// myMemberというメンバのみimportする import {myMember} from "my-module";
// asキーワードで別名を指定できる import {reallyReallyLongModuleMemberName as shortName} from "my-module";
* ただし、現在殆どのブラウザでは動作できないので、トランスコンパイラ上でのみ体現できる
とりあえずES6を試してみるには
ブラウザ上でJSのソースの動作検証が行えるツールを利用する
個人的にはjsbinが一番オススメ