EagleLand

2013.12.26

ES6のシンタックスを予習復習(1) ~let, const, Arrow Function, Generators, for of~

ES6のフォローについては書こうとしていたものの後手に回っていて、ようやく書くに至る。SetやらMapやら、追加クラスのあたりは実装されても試そうとした時にそんなに障壁にならない気がしてるけど、letとかconst、アローファンクション等々、シンタックスが関わる辺はつっかえ棒になりかねないので消化しておく。

この段階での実装状況はNightly > Canary

この2つをECMAScript 6 compatibility tableで比較するとNightlyのほうが先行実装は進んでいるのでNightlyでアレコレする。Canaryだと試したいシンタックス部分がまだ実装されていないので断念。

Canaryでデバッグしたい人はES6実装を有効にするフラグをたてる必要があるので、chrome://flagsにアクセスして#enable-javascript-harmonyでページを検索すると JavaScript の試験運用機能を有効にする という項目があるのでそれを有効にする。あとはDevTool開いてConsoleでいつも通り試せる。が、今回はFirefoxなのでScratchpadでやる。使い慣れてない…。

let

letを使うことでブロックスコープの変数宣言が可能になる。

まず、今までのvarを使ったパターン。(これだとそもそもlintで怒られるけど、挙動のテストということで。)

var x = 1;
if (true) {
  var x = 2;
  console.log('x is ' + x);
  // x is 2
}
console.log('x is ' + x);
// x is 2

このケースだと最初に宣言したxがif内で上書きされ、2回とも2が出力される。ここでletを使ってみる。

let x = 1;
if (true) {
  let x = 2;
  console.log('x is ' + x);
  // x is 2
}
console.log('x is ' + x);
// x is 1

期待通りの動きをしてくれる。varletの複合はどうなるか。

let x = 1;
if (true) {
  var x = 2;
  console.log('x is ' + x);
  // x is 2
}
console.log('x is ' + x);
// x is 2

varはスコープの外のletを上書いてしまうらしい。

var x = 1;
if (true) {
  let x = 2;
  console.log('x is ' + x);
  // x is 2
}
console.log('x is ' + x);
// x is 1

こっちはスコープを守ってくれている。varの使用頻度えらく減る予感…。おなじみのスコープ形成無名関数も減るのかな、と。なんかFirefoxの現在の実装は仕様とちょっと違うらしい

const

constもES6から追加される新しい変数宣言の方法で、定数値(constant value)を宣言する時に使用する。constを使って宣言された変数には値を代入できなくなる。

const PI = 3.14;
PI = 3;
console.log('PI is ' + PI);
// PI is 3.14

こういうのものOK。

var x = 3;
var y = 0.14;
const PI = x + y;
PI = 3;
console.log('PI is ' + PI);
// PI is 3.14

use strictをつけてstrictモードになっている場合は、PI = 3;の部分で例外が発生。ちなみにこれはtry/catchでも捕捉出来なかった(?)。

"use strict";
const PI = 3.14;
PI = 3;
// TypeError: PI is read-only
console.log(PI);
// ここまで来ない

関数のアロー記法

関数を、functionの代わりに=>を使って表現可能になったアレ。CoffeeScriptを連想させられる。

const square = function(x) {
  return x * x;
};
const multiply = function(x, y) {
  return x * y;
}
console.log(square(2));
// 4
console.log(multiply(3, 4));
// 12

これをアロー記法で書くと

const square = x => {
  return x * x;
};
const multiply = (x, y) => {
  return x * y;
}
console.log(square(2));
// 4
console.log(multiply(3, 4));
// 12

関数のとる引数がひとつの場合は括弧を省略出来る。thisの持ち込みには注意するとして、記法自体は functionというキーワードを除いて、引数の括弧の後ろに=>をつける という覚え方で良いのかな。あとはイベントのコールバックにも書いたり出来そう。

document.addEventListener('DOMContentLoaded', e => {
  console.log(e);
});
// DOMContentLoaded {target: ...}

C#のラムダ式で妙に親しみがあるのでちょっと懐かしい。そしてJSで書けるのは嬉しい。

Generatorsとfor ofループ

Generator Functionは実行された後も実行状態を保持し、イテレータとして使用する。フィボナッチ数列を例に説明。

const fibonacci = function* () {
  let prev = 0;
  let curr = 1;
  while (true) {
    prev = curr;
    curr = prev + curr;
    yield curr;
  }
};
// SyntaxError: missing ; before statement

が、SpiderMonkeyがfunction*シンタックスに綺麗に対応していない(?)ようで、実行出来なかった。仕方なく以下のように書いた。

const fibonacci = function* () {
  var prev = 0;
  var curr = 1;
  var swap = null;
  while (true) {
    swap = curr;
    curr = prev + curr;
    prev = swap;
    yield curr;
  }
};
var seq = fibonacci();
console.log(seq.next()); // {value: 1, done: false}
console.log(seq.next()); // {value: 2, done: false}
console.log(seq.next()); // {value: 3, done: false}
console.log(seq.next()); // {value: 5, done: false}
console.log(seq.next()); // {value: 8, done: false}

実行するとイテレータオブジェクトを返却して、next()を実行すると値が列挙されていく。これを配列のイテレーションをすることが可能なfor ofループと組み合わせてみる。

for (var seq of fibonacci()) {
  if (seq > 1000) {
    break;
  }
  console.log(seq);
}
// 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987

順番が逆になってしまいましたが、for ofで配列をイテレートすることが可能。今までやっていたfor in + Arrayの罠にハマることがなくなりそう。

var array = [1, 10, 100, 1000, 10000];
for (var item of array) {
  console.log(item);
}
// 1, 10, 100, 1000, 10000

オブジェクトにはイテレータ関数が明示されていないので、そのままだとfor ofは使えない。なので、イテレータ関数を挟んでfor ofに列挙させてみる。

var data = {
  a: 10,
  b: 20,
  c: 30
};
const valueIterator = function* (object) {
  var keys = Object.keys(object);
  for(var i = 0, len = keys.length;i < len;i++) {
    yield object[keys[i]];
  }
};
for (var item of valueIterator(data)) {
  console.log(item);
}
// 10, 20, 30

所感

一番試したかったclassが未実装で書けなかった(ただの糖衣構文だが)。でも実装も色々と進んできているし、試すレベルでは色々と楽しめそうな予感。

ES6をES5にコンパイルしてくれるgoogle / traceur-compilerとかもあるが、プロダクトで 「ES6をゴリゴリ使って後方互換にES5ソースを用意する」 みたいなことをするのはちょっとだけ早いかなとは思った。

続き(?)はまた新年。