のんびりSEの議事録

プログラミング系のポストからアプリに関してのポストなどをしていきます。まれにアニメ・マンガなど

今更ながら速攻で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. 定数の値として利用する
  2. 非公開なプロパティを定義する

  3. 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

複数の非同期処理を並列に実行し、その全てが成功した場合に、処理を実行

qiita.com

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でのHashPythonでの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が一番オススメ

JS Bin - Collaborative JavaScript Debugging

ES6 Fiddle

jsdo.it - Share JavaScript, HTML5 and CSS