以下はGithubで2万☆を獲得している、Overview of ECMAScript 6 featuresというコンテンツの日本語訳です。
Introduction
ECMAScript2015とも言われているECMAScript6は、ECMAScript標準の最新バージョンです。
ES6は、2009年に標準化されたES5以来、初めての重要なアップデートになります。
主要なJavaScriptエンジンにおいて、現在これら新機能の実装が進行中です。
ES6の完全な仕様については、ES6 standardを参照してください。
ES6には、以下のような新機能が含まれています。
ECMAScript 6 Features
arrows
アロー=>
は、function
の省略表記で、C#、Java8やCoffeeScriptのアローと似たような構文で書けるよ。
式と文どちらの書き方もサポートするよ。
呼び出し方でthis
が変わるfunction
とは違い、アローのthis
は常にその周囲と同じスコープになるよ。
// 式
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
var pairs = evens.map(v => ({even: v, odd: v + 1}));
// 文
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
// thisのスコープ
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
}
より詳細はMDN Arrow Functionsを参照すること。
classes
ES6のクラス構文は、プロトタイプベース言語をオブジェクト指向に見せかけるシンタックスシュガーです。
ひとつ便利な定義を追加することで、クラス構文が使いやすくなり相互運用性も高まります。
クラスは継承、super、インスタンスメソッドとクラスメソッド、コンストラクタをサポートします。
class SkinnedMesh extends THREE.Mesh {
// コンストラクタ
constructor(geometry, materials) {
// super.constructor()
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
// インスタンスメソッド
update(camera) {
//...
super.update();
}
// getter
get boneCount() {
return this.bones.length;
}
//setter
set matrixType(matrixType) {
this.idMatrix = SkinnedMesh[matrixType]();
}
// クラスメソッド
static defaultMatrix() {
return new THREE.Matrix4();
}
}
より詳細はMDN Classesを参照すること。
enhanced object literals
オブジェクトリテラルはfoo:foo
の省略、メソッド定義、super()
、プロパティ名の演算などに対応しました。
オブジェクトリテラルとクラス構文を一緒に利用することで、これらがより便利に扱えるようになります。
var obj = {
// __proto__
__proto__: theProtoObj,
// handler: handlerの略
handler,
// メソッド
toString() {
// theProtoObj.toString()を呼んでる
return "d " + super.toString();
},
// 計算結果をプロパティ名にできる
[ 'prop_' + (() => 42)() ]: 42
};
より詳細はMDN Grammar and types: Object literalsを参照すること。
template strings
テンプレートリテラルは文字列を作成するためのシンタックスシュガーです。
これはPerlやPythonなどのヒアドキュメントと似ています。
オプションで、タグを付けることで文字列作成をカスタマイズすることができます。
インジェクション攻撃を防いだり、より高度な文字列作成ができるでしょう。
// 基本
`In JavaScript '\n' is a line-feed.`
// 複数行
`In JavaScript this is
not legal.`
// 文字列展開
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// POST関数が呼ばれる
POST`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);
より詳細はMDN Template Stringsを参照すること。
destructuring
分割代入は配列とオブジェクトによるバインディングをサポートしています。
分割代入はfoo["bar"]
の参照と同じようにフェールセーフで、値がなかったらundefined
を返します。
// 配列
var [a, , b] = [1,2,3];
// オブジェクト
var { op: a, lhs: { op: b }, rhs: c }
= getASTNode()
// オブジェクトの短縮形
var {op, lhs, rhs} = getASTNode()
// 引数
function g({name: x}) {
console.log(x);
}
g({name: 5})
// フェールソフト
var [a] = []; // a === undefined
// デフォルト値があればそっちになる
var [a = 1] = []; // a === 1
より詳細はMDN Destructuring assignmentを参照すること。
default + rest + spread
関数呼び出し時に引数が無かった場合はデフォルト値が使用される。。
呼び出される方に書く...
はRest parametersと呼ばれ、余った引数が全て配列で入ってくる。
呼び出す方に書く...
はスプレッド演算子と呼ばれ、配列を展開して呼び出し先に渡される。
function f(x, y=12) {
// 第二引数が無いかundefinedのときはy=12になる
return x + y;
}
f(3) // 15
function f(x, ...y) {
// y = ["hello", true]になる
return x * y.length;
}
f(3, "hello", true) // 6
function f(x, y, z) {
return x + y + z;
}
// f(1, 2, 3)と同じ
f(...[1, 2, 3]) // 6
より詳細はMDN Default parameters、Rest parameters、Spread Operatorを参照すること。
let + const
letはブロックスコープのvarで再代入可能。
constは再代入不可能。
function f() {
{
let x;
{
// {}内のスコープになるので定義可能
const x = "sneaky";
// constは再代入不可なのでエラーになる
x = "foo";
}
// 同スコープで既にletしてるのでエラーになる
let x = "inner";
// 再代入はOK
x = "inner";
}
}
より詳細はMDN let statement、const statementを参照すること。
iterators + for..of
イテレータはIEnumerableやIterableのように反復処理をカスタマイズできます。
for-of
を使うと、for-in
より簡単にイテレータから値を取り出せます。
特に何もしなくてもLINQのように遅延評価されます。
let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { done: false, value: cur }
}
}
}
}
for (var n of fibonacci) {
// 1000で終了
if (n > 1000)
break;
console.log(n);
}
イテレータのinterfaceをTypeScriptで書くと以下のようになります。
interface IteratorResult {
done: boolean;
value: any;
}
interface Iterator {
next(): IteratorResult;
}
interface Iterable {
[Symbol.iterator](): Iterator
}
より詳細はMDN for...ofを参照すること。
generators
ジェネレータは、function*
とyield
を使ってイテレータを簡単に作れる機能です。
function*
で宣言した関数はGeneratorインスタンスを返します。
ジェネレータはnext
とthrow
を持ったイテレータのサブタイプで、yield
で関数は動いたまま値を返します(もしくはthrowする)。
注:イテレータを使うとES7のawait
みたいなこともできるよ
var fibonacci = {
[Symbol.iterator]: function*() {
var pre = 0, cur = 1;
for (;;) {
var temp = pre;
pre = cur;
cur += temp;
yield cur;
}
}
}
for (var n of fibonacci) {
// 1000で終了
if (n > 1000)
break;
console.log(n);
}
ジェネレータのinterfaceをTypeScriptで書くと以下のようになります。
interface Generator extends Iterator {
next(value?: any): IteratorResult;
throw(exception: any);
}
より詳細はMDN Iteration protocolsを参照すること。
unicode
ユニコードリテラル、RegExpのUnicodeフラグ、21ビットのコードポイントAPIなど、Unicodeサポートを強化しました。
これらにより、JavaScriptでグローバルなアプリ作成が容易になります。
// これまでと同じ
"𠮷".length == 2
// Unicodeフラグ
"𠮷".match(/./u)[0].length == 2
// Unicodeリテラル
"\u{20BB7}"=="𠮷"=="\uD842\uDFB7"
// String.prototype.codePointAt()
"𠮷".codePointAt(0) == 0x20BB7
// イテレータ
for(var c of "𠮷") {
console.log(c);
}
より詳細はMDN RegExp.prototype.unicodeを参照すること。
modules
コンポーネント定義のモジュールを言語レベルでサポートしました。
一般的なAMDやCommonJSみたいな形で書けます。
モジュールは非同期で、必要とされるまで中のコードは実行されません。
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));
追加機能としては、export default
とexport *
があります。
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.log(x);
}
// app.js
import ln, {pi, e} from "lib/mathplusplus";
alert("2π = " + ln(e)*pi*2);
より詳細はMDN import statement、export statementを参照すること。
module loaders
モジュールローダは以下をサポートします。
・動的ローディング
・状態の分離
・グローバル領域を使わない
・コンパイル時フック
・仮想化のネスト
デフォルトのローダを変更することができ、また新しいローダを作成することもできます。
// 動的ローディング 'System'はデフォルトのローダ
System.import('lib/math').then(function(m) {
alert("2π = " + m.sum(m.pi, m.pi));
});
// 新しいローダ
var loader = new Loader({
global: fixup(window) // 'console.log'を差し替える
});
loader.eval("console.log('hello world!');");
// モジュールを直接操作
System.get('jquery');
System.set('jquery', Module({$: $})); // WARNING: not yet finalized
map + set + weakmap + weakset
一般的なアルゴリズムに対して効率的なデータ構造。
WeakMapはメモリリークのない、オブジェクトをキーとするKey-Valueストアです。
// Set
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
// Map
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
// WeakMap
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
// WeakSet
var ws = new WeakSet();
ws.add({ data: 42 });
// Weak*以外から参照がない場合、キーはGCされる
より詳細はMDN Map、Set、WeakMap、WeakSetを参照すること。
proxies
Proxyを使うと、おおよそあらゆるオブジェクトを上書きして挙動を変更することが可能になります。
傍受、オブジェクトの仮想化、ロギングなどに使用できるでしょう。
// 普通のObject
var target = {};
var handler = {
get: function (receiver, name) {
return `Hello, ${name}!`;
}
};
// targetをhandlerで上書き
var p = new Proxy(target, handler);
p.world === 'Hello, world!';
// 普通の関数
var target = function () { return 'I am the target'; };
var handler = {
apply: function (receiver, ...args) {
return 'I am the proxy';
}
};
// targetをhandlerで上書き
var p = new Proxy(target, handler);
p() === 'I am the proxy';
ランタイムレベルの操作も上書きできます。
var handler =
{
get:...,
set:...,
has:...,
deleteProperty:...,
apply:...,
construct:...,
getOwnPropertyDescriptor:...,
defineProperty:...,
getPrototypeOf:...,
setPrototypeOf:...,
enumerate:...,
ownKeys:...,
preventExtensions:...,
isExtensible:...
}
より詳細はMDN Proxyを参照すること。
symbols
SymbolはObjectに対するアクセス制御を可能にします。
SymbolはObjectのキーとして設定できる、新しいプリミティブ型です。
引数descriptionを渡せますが、必須ではありません。
Symbolはgensymのように一意ですが、Object.getOwnPropertySymbolsで列挙できるのでprivateではありません。
var MyClass = (function() {
// Symbol
var key = Symbol("key");
// キーにする
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
... this[key] ...
}
};
return MyClass;
})();
var c = new MyClass("hello");
// MyClass.keyとは別物なので参照不可
c["key"] === undefined;
より詳細はMDN Symbolを参照すること。
subclassable built-ins
ES6ではArray、Date、DOM Elementsのような組み込み関数を継承できます。
オブジェクト生成時にCtor
という関数が呼ばれ、以下の処理を行います。
・Ctor[@@create]
を呼んでオブジェクトを生成する
・constructor
を呼んでインスタンスを生成する
Symbol.create
で既知の@@create
を利用できます。
組み込み関数は@@create
を公開しています。
// Arrayの擬似コード
class Array {
constructor(...args) { /* ... */ }
static [Symbol.create]() {
// Install special [[DefineOwnProperty]] to magically update 'length'
}
}
// Arrayのサブクラス
class MyArray extends Array {
constructor(...args) { super(...args); }
}
// newすると`@@create`、`constructor`を順に呼び出す
var arr = new MyArray();
arr[1] = 12;
arr.length == 2
math + number + string + array + object APIs
Mathライブラリ、配列変換、文字列操作、Object.assignなど多くのライブラリが追加されました。
Number.EPSILON;
Number.isInteger(Infinity); // false
Number.isNaN("NaN"); // false
Math.acosh(3); // 1.762747174039086
Math.hypot(3, 4); // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2); // 2
"abcde".includes("cd"); // true
"abc".repeat(3); // "abcabcabc"
Array.from(document.querySelectorAll('*')); // Arrayを返す
Array.of(1, 2, 3); // new Array()とほぼ同じだが、引数が一つのときに動作が変わらない
[0, 0, 0].fill(7, 1); // [0,7,7]
[1, 2, 3].find(x => x == 3); // 3
[1, 2, 3].findIndex(x => x == 2); // 1
[1, 2, 3, 4, 5].copyWithin(3, 0); // [1, 2, 3, 1, 2]
["a", "b", "c"].entries(); // [0, "a"], [1,"b"], [2,"c"]のイテレータ
["a", "b", "c"].keys(); // 0, 1, 2のイテレータ
["a", "b", "c"].values(); // "a", "b", "c"のイテレータ
Object.assign(Point, { origin: new Point(0,0) })
より詳細はMDN Number、Math、Array.from、Array.of、Array.prototype.copyWithin、Object.assignを参照すること。
binary and octal literals
数値リテラルに2進数表記bと8進数表記oが追加されました。
0b111110111 === 503 // true
0o767 === 503 // true
promises
Promiseは非同期処理を行う第一級関数ライブラリです。
Promiseは既に多くのJavaScriptライブラリで使用されています。
function timeout(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
})
}
var p = timeout(1000).then(() => {
return timeout(2000);
}).then(() => {
throw new Error("hmm");
}).catch(err => {
return Promise.all([timeout(100), timeout(200)]);
})
より詳細はMDN Promiseを参照すること。
reflect api
リフレクションAPIはObjectに対するメタ操作を可能とします。
これはProxy APIの逆みたいなもので、Proxy同様呼び出しをフックできます。
Proxyの実装に特に便利です。
// サンプルはないよ
より詳細はMDN Reflectを参照すること。
tail calls
末尾再帰を最適化します。
再帰的なアルゴリズムに無限の入力があっても今後は安心です。
function factorial(n, acc = 1) {
'use strict';
if (n <= 1) return acc;
return factorial(n - 1, n * acc);
}
// 今はStack overflowが起こるけど、ES6では安全になる
factorial(100000)
感想
PHP5.0からPHP5.3に進化したみたいなかんじ?
正直ES6よくわかってないので、読んでもわからないところが多かった。
というかこのGithub全体的にあまりに端折りすぎてて、詳細を見ない限り機能が存在することくらいしかわからない。
さらに更新が一年前から止まっていて、大量のプルリクも放置状態。
どうして2万も☆がついてるのかよくわからない。
あとCtor[@@create]
が何なのか全くわからなかった。