この章では、本書のすべての章の概要セクションをまとめています。
Math
機能Number
プロパティMath
メソッドlet
const
...
)Object
の新しいメソッドfor-of
ループthen()
呼び出しのチェーンES6仕様の導入部には、すべての新機能がリストされています。
[ECMAScript 6]の主な拡張機能には、モジュール、クラス宣言、レキシカルブロックスコープ、イテレータとジェネレーター、非同期プログラミングのためのPromise、分割代入パターン、適切な末尾呼び出しが含まれます。組み込みのECMAScriptライブラリは、マップ、セット、およびバイナリ数値の配列を含む追加のデータ抽象化をサポートするために拡張されており、文字列と正規表現のUnicode補助文字の追加サポートも提供されています。組み込みは、サブクラス化を介して拡張可能になりました。
機能には3つの主要なカテゴリがあります。
Math
機能 バイナリ表記と8進数表記で整数を指定できるようになりました。
> 0xFF // ES5: hexadecimal
255
> 0b11 // ES6: binary
3
> 0o10 // ES6: octal
8
Number
プロパティ グローバルオブジェクトNumber
にいくつかの新しいプロパティが追加されました。
Number.EPSILON
は、丸め誤差に対する許容範囲で浮動小数点数を比較します。Number.isInteger(num)
は、num
が整数(小数部を持たない数値)であるかどうかをチェックします。
> Number.isInteger(1.05)
false
> Number.isInteger(1)
true
> Number.isInteger(-3.1)
false
> Number.isInteger(-3)
true
Number.isSafeInteger(number)
Number.MIN_SAFE_INTEGER
Number.MAX_SAFE_INTEGER
Number.isNaN(num)
は、num
が値NaN
であるかどうかをチェックします。グローバル関数isNaN()
とは対照的に、引数を数値に強制変換しないため、数値以外の場合でも安全です。
> isNaN('???')
true
> Number.isNaN('???')
false
Number
の3つの追加メソッドは、同じ名前のグローバル関数とほぼ同等です:Number.isFinite
、Number.parseFloat
、Number.parseInt
。Math
メソッド グローバルオブジェクトMath
には、数値演算、三角関数、ビット単位演算のための新しいメソッドがあります。4つの例を見てみましょう。
Math.sign()
は数値の符号を返します。
> Math.sign(-8)
-1
> Math.sign(0)
0
> Math.sign(3)
1
Math.trunc()
は数値の小数部を削除します。
> Math.trunc(3.1)
3
> Math.trunc(3.9)
3
> Math.trunc(-3.1)
-3
> Math.trunc(-3.9)
-3
Math.log10()
は、底が10の対数を計算します。
> Math.log10(100)
2
Math.hypot()
は、引数の2乗の合計の平方根(ピタゴラスの定理)を計算します。
> Math.hypot(3, 4)
5
新しい文字列メソッド
> 'hello'.startsWith('hell')
true
> 'hello'.endsWith('ello')
true
> 'hello'.includes('ell')
true
> 'doo '.repeat(3)
'doo doo doo '
ES6には、新しい種類の文字列リテラルであるテンプレートリテラルがあります。
// String interpolation via template literals (in backticks)
const
first
=
'Jane'
;
const
last
=
'Doe'
;
console
.
log
(
`Hello
${
first
}
${
last
}
!`
);
// Hello Jane Doe!
// Template literals also let you create strings with multiple lines
const
multiLine
=
`
This is
a string
with multiple
lines`
;
シンボルは、ECMAScript 6の新しいプリミティブ型です。ファクトリ関数を介して作成されます。
const
mySymbol
=
Symbol
(
'mySymbol'
);
ファクトリ関数を呼び出すたびに、新しく一意のシンボルが作成されます。オプションのパラメータは、シンボルを印刷するときに表示される説明的な文字列です(他の目的はありません)。
> mySymbol
Symbol(mySymbol)
シンボルは主に一意のプロパティキーとして使用されます。シンボルは、他のプロパティキー(シンボルまたは文字列)と衝突することはありません。たとえば、Symbol.iterator
に格納されているシンボルをメソッドのキーとして使用することにより、オブジェクトをイテラブル(for-of
ループおよびその他の言語メカニズムを介して使用可能)にすることができます(イテラブルの詳細については、イテレーションの章で説明します)。
const
iterableObject
=
{
[
Symbol
.
iterator
]()
{
// (A)
···
}
}
for
(
const
x
of
iterableObject
)
{
console
.
log
(
x
);
}
// Output:
// hello
// world
A行では、シンボルがメソッドのキーとして使用されています。この一意のマーカーにより、オブジェクトはイテラブルになり、for-of
ループを使用できるようになります。
ECMAScript 5では、色などの概念を表すために文字列を使用していた可能性があります。ES6では、シンボルを使用すると、常に一意であることが保証されます。
const
COLOR_RED
=
Symbol
(
'Red'
);
const
COLOR_ORANGE
=
Symbol
(
'Orange'
);
const
COLOR_YELLOW
=
Symbol
(
'Yellow'
);
const
COLOR_GREEN
=
Symbol
(
'Green'
);
const
COLOR_BLUE
=
Symbol
(
'Blue'
);
const
COLOR_VIOLET
=
Symbol
(
'Violet'
);
function
getComplement
(
color
)
{
switch
(
color
)
{
case
COLOR_RED
:
return
COLOR_GREEN
;
case
COLOR_ORANGE
:
return
COLOR_BLUE
;
case
COLOR_YELLOW
:
return
COLOR_VIOLET
;
case
COLOR_GREEN
:
return
COLOR_RED
;
case
COLOR_BLUE
:
return
COLOR_ORANGE
;
case
COLOR_VIOLET
:
return
COLOR_YELLOW
;
default
:
throw
new
Exception
(
'Unknown color: '
+
color
);
}
}
Symbol('Red')
を呼び出すたびに、新しいシンボルが作成されます。したがって、COLOR_RED
を別の値と間違えることはありません。それが文字列'Red'
であった場合とは異なります。
シンボルを文字列に強制変換(暗黙的に変換)すると、例外がスローされます。
const
sym
=
Symbol
(
'desc'
);
const
str1
=
''
+
sym
;
// TypeError
const
str2
=
`
${
sym
}
`
;
// TypeError
唯一の解決策は、明示的に変換することです。
const
str2
=
String
(
sym
);
// 'Symbol(desc)'
const
str3
=
sym
.
toString
();
// 'Symbol(desc)'
強制変換を禁止すると、一部のエラーが防止されますが、シンボルの操作も複雑になります。
次の操作は、プロパティキーとしてシンボルを認識します。
Reflect.ownKeys()
[]
を介したプロパティアクセスObject.assign()
次の操作は、プロパティキーとしてシンボルを無視します。
Object.keys()
Object.getOwnPropertyNames()
for-in
ループES6には、テンプレートリテラルとタグ付きテンプレートリテラルの2つの新しい種類のリテラルがあります。これらの2つのリテラルは似たような名前で見た目も似ていますが、まったく異なります。したがって、区別することが重要です。
テンプレートリテラルは、複数行にまたがり、補間式(${···}
を介して挿入)を含むことができる文字列リテラルです。
const
firstName
=
'Jane'
;
console
.
log
(
`Hello
${
firstName
}
!
How are you
today?`
);
// Output:
// Hello Jane!
// How are you
// today?
タグ付きテンプレートリテラル(略してタグ付きテンプレート)は、テンプレートリテラルの前に関数を記述することによって作成されます。
> String.raw`A \tagged\ template`
'A \\tagged\\ template'
タグ付きテンプレートは関数呼び出しです。前の例では、タグ付きテンプレートの結果を生成するために、メソッドString.raw
が呼び出されます。
ES6では、変数を宣言する2つの新しい方法であるlet
とconst
が提供されます。これらは主に、ES5での変数の宣言方法であるvar
に代わるものです。
let
let
は var
と同様に動作しますが、宣言された変数がブロックスコープを持つ点が異なります。つまり、現在のブロック内でのみ存在します。一方、var
は関数スコープです。
次のコードでは、let
で宣言された変数 tmp
が、A行から始まるブロック内でのみ存在することがわかります。
function
order
(
x
,
y
)
{
if
(
x
>
y
)
{
// (A)
let
tmp
=
x
;
x
=
y
;
y
=
tmp
;
}
console
.
log
(
tmp
===
x
);
// ReferenceError: tmp is not defined
return
[
x
,
y
];
}
const
const
は let
と同様に動作しますが、宣言する変数はすぐに初期化する必要があり、その後値を変更することはできません。
const
foo
;
// SyntaxError: missing = in const declaration
const
bar
=
123
;
bar
=
456
;
// TypeError: `bar` is read-only
for-of
はループの反復ごとに1つのバインディング(変数のためのストレージスペース)を作成するため、ループ変数を const
で宣言しても問題ありません。
for
(
const
x
of
[
'a'
,
'b'
])
{
console
.
log
(
x
);
}
// Output:
// a
// b
次の表は、ES6で変数を宣言できる6つの方法の概要を示しています(kangax氏による表に触発されました)。
ホイスティング | スコープ | グローバルプロパティを作成するか | |
---|---|---|---|
var |
宣言 | 関数 | はい |
let |
一時的なデッドゾーン | ブロック | いいえ |
const |
一時的なデッドゾーン | ブロック | いいえ |
関数 |
完全 | ブロック | はい |
class |
いいえ | ブロック | いいえ |
import |
完全 | モジュールグローバル | いいえ |
分割代入 は、(ネストされている可能性のある)オブジェクトや配列に格納されたデータから複数の値を抽出する便利な方法です。データの受け渡し場所(代入の左辺など)で使用できます。値の抽出方法はパターンによって指定されます(例については以下を参照)。
オブジェクトの分割代入
const
obj
=
{
first
:
'Jane'
,
last
:
'Doe'
};
const
{
first
:
f
,
last
:
l
}
=
obj
;
// f = 'Jane'; l = 'Doe'
// {prop} is short for {prop: prop}
const
{
first
,
last
}
=
obj
;
// first = 'Jane'; last = 'Doe'
分割代入は戻り値の処理に役立ちます
const
obj
=
{
foo
:
123
};
const
{
writable
,
configurable
}
=
Object
.
getOwnPropertyDescriptor
(
obj
,
'foo'
);
console
.
log
(
writable
,
configurable
);
// true true
配列の分割代入(すべての反復可能な値に対して機能します)
const
iterable
=
[
'a'
,
'b'
];
const
[
x
,
y
]
=
iterable
;
// x = 'a'; y = 'b'
分割代入は戻り値の処理に役立ちます
const
[
all
,
year
,
month
,
day
]
=
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.
exec
(
'2999-12-31'
);
分割代入は以下の場所で使用できます(デモンストレーションのために配列パターンを示しています。オブジェクトパターンも同様に機能します)。
// Variable declarations:
const
[
x
]
=
[
'a'
];
let
[
x
]
=
[
'a'
];
var
[
x
]
=
[
'a'
];
// Assignments:
[
x
]
=
[
'a'
];
// Parameter definitions:
function
f
([
x
])
{
···
}
f
([
'a'
]);
for-of
ループ内でも分割代入を使用できます
const
arr
=
[
'a'
,
'b'
];
for
(
const
[
index
,
element
]
of
arr
.
entries
())
{
console
.
log
(
index
,
element
);
}
// Output:
// 0 a
// 1 b
ECMAScript 6では、パラメータ処理が大幅にアップグレードされました。パラメータのデフォルト値、レストパラメータ(可変長引数)、分割代入がサポートされるようになりました。
さらに、スプレッド演算子は、関数/メソッド/コンストラクタ呼び出しと配列リテラルで役立ちます。
デフォルトパラメータ値は、等号(=
)を介してパラメータに指定されます。呼び出し元がパラメータの値を提供しない場合、デフォルト値が使用されます。次の例では、y
のデフォルトパラメータ値は0です。
function
func
(
x
,
y
=
0
)
{
return
[
x
,
y
];
}
func
(
1
,
2
);
// [1, 2]
func
(
1
);
// [1, 0]
func
();
// [undefined, 0]
パラメータ名の前にレスト演算子(...
)を付けると、そのパラメータは配列を介して残りのすべてのパラメータを受け取ります。
function
format
(
pattern
,
...
params
)
{
return
{
pattern
,
params
};
}
format
(
1
,
2
,
3
);
// { pattern: 1, params: [ 2, 3 ] }
format
();
// { pattern: undefined, params: [] }
パラメータリストでオブジェクトパターンを使用して分割代入すると、名前付きパラメータをシミュレートできます。
function
selectEntries
({
start
=
0
,
end
=-
1
,
step
=
1
}
=
{})
{
// (A)
// The object pattern is an abbreviation of:
// { start: start=0, end: end=-1, step: step=1 }
// Use the variables `start`, `end` and `step` here
···
}
selectEntries
({
start
:
10
,
end
:
30
,
step
:
2
});
selectEntries
({
step
:
3
});
selectEntries
({});
selectEntries
();
A行の = {}
を使用すると、パラメータなしで selectEntries()
を呼び出すことができます。
...
) 関数とコンストラクタの呼び出しでは、スプレッド演算子は反復可能な値を引数に変換します。
>
Math
.
max
(
-
1
,
5
,
11
,
3
)
11
>
Math
.
max
(...[
-
1
,
5
,
11
,
3
])
11
>
Math
.
max
(
-
1
,
...[
-
5
,
11
],
3
)
11
配列リテラルでは、スプレッド演算子は反復可能な値を配列要素に変換します。
>
[
1
,
...[
2
,
3
],
4
]
[
1
,
2
,
3
,
4
]
ES5では、単一の構成要素である(従来の)関数が3つの役割を果たしていました。
ES6では、より特化が進んでいます。3つの役割は、現在次のように処理されています。関数の定義とクラスの定義に関する限り、定義は宣言または式になります。
特にコールバックの場合、アロー関数は周囲のスコープの this
を隠蔽しないため便利です。
長いコールバックやスタンドアロン関数の場合、従来の関数でも問題ありません。一部のAPIでは、暗黙のパラメータとして this
を使用します。その場合、従来の関数を使用するしかありません。
私は以下を区別することに注意してください。
(後で説明するように)動作が異なるにもかかわらず、これらのエンティティはすべて関数です。例えば
> typeof (() => {}) // arrow function
'function'
> typeof function* () {} // generator function
'function'
> typeof class {} // class
'function'
アロー関数には2つの利点があります。
1つ目は、従来の関数式よりも冗長でないことです。
const
arr
=
[
1
,
2
,
3
];
const
squares
=
arr
.
map
(
x
=>
x
*
x
);
// Traditional function expression:
const
squares
=
arr
.
map
(
function
(
x
)
{
return
x
*
x
});
2つ目は、その this
が周囲(レキシカル)から取得されることです。したがって、bind()
や that = this
はもう必要ありません。
function
UiComponent
()
{
const
button
=
document
.
getElementById
(
'myButton'
);
button
.
addEventListener
(
'click'
,
()
=>
{
console
.
log
(
'CLICK'
);
this
.
handleClick
();
// lexical `this`
});
}
次の変数はすべてアロー関数内でレキシカルです。
arguments
super
this
new.target
メソッド定義
const
obj
=
{
myMethod
(
x
,
y
)
{
···
}
};
プロパティ値の短縮形
const
first
=
'Jane'
;
const
last
=
'Doe'
;
const
obj
=
{
first
,
last
};
// Same as:
const
obj
=
{
first
:
first
,
last
:
last
};
算出されたプロパティキー
const
propKey
=
'foo'
;
const
obj
=
{
[
propKey
]
:
true
,
[
'b'
+
'ar'
]
:
123
};
この新しい構文は、メソッド定義にも使用できます
const
obj
=
{
[
'h'
+
'ello'
]()
{
return
'hi'
;
}
};
console
.
log
(
obj
.
hello
());
// hi
算出されたプロパティキーの主な使用例は、シンボルをプロパティキーとして簡単に使用できるようにすることです。
Object
の新しいメソッド Object
の最も重要な新しいメソッドは assign()
です。従来、この機能はJavaScriptの世界では extend()
と呼ばれていました。この従来の操作とは対照的に、Object.assign()
は独自の(継承されていない)プロパティのみを考慮します。
const
obj
=
{
foo
:
123
};
Object
.
assign
(
obj
,
{
bar
:
true
});
console
.
log
(
JSON
.
stringify
(
obj
));
// {"foo":123,"bar":true}
クラスとサブクラス
class
Point
{
constructor
(
x
,
y
)
{
this
.
x
=
x
;
this
.
y
=
y
;
}
toString
()
{
return
`(
${
this
.
x
}
,
${
this
.
y
}
)`
;
}
}
class
ColorPoint
extends
Point
{
constructor
(
x
,
y
,
color
)
{
super
(
x
,
y
);
this
.
color
=
color
;
}
toString
()
{
return
super
.
toString
()
+
' in '
+
this
.
color
;
}
}
クラスの使用
> const cp = new ColorPoint(25, 8, 'green');
> cp.toString();
'(25, 8) in green'
> cp instanceof ColorPoint
true
> cp instanceof Point
true
内部的には、ES6クラスは根本的に新しいものではありません。主に、古いスタイルのコンストラクタ関数を作成するためのより便利な構文を提供します。typeof
を使用すると、それが確認できます。
> typeof Point
'function'
JavaScriptには長い間モジュールがありましたが、言語に組み込まれたものではなく、ライブラリを介して実装されていました。ES6は、JavaScriptに組み込みモジュールが搭載された初めてのバージョンです。
ES6モジュールはファイルに格納されます。ファイルごとに1つのモジュールがあり、モジュールごとに1つのファイルがあります。モジュールからものをエクスポートする方法は2つあります。これら2つの方法は混合できますが、通常は別々に使用することをお勧めします。
複数の名前付きエクスポートが可能です。
//------ lib.js ------
export
const
sqrt
=
Math
.
sqrt
;
export
function
square
(
x
)
{
return
x
*
x
;
}
export
function
diag
(
x
,
y
)
{
return
sqrt
(
square
(
x
)
+
square
(
y
));
}
//------ main.js ------
import
{
square
,
diag
}
from
'lib'
;
console
.
log
(
square
(
11
));
// 121
console
.
log
(
diag
(
4
,
3
));
// 5
モジュール全体をインポートすることもできます。
//------ main.js ------
import
*
as
lib
from
'lib'
;
console
.
log
(
lib
.
square
(
11
));
// 121
console
.
log
(
lib
.
diag
(
4
,
3
));
// 5
単一のデフォルトエクスポートが可能です。例えば、関数
//------ myFunc.js ------
export
default
function
()
{
···
}
// no semicolon!
//------ main1.js ------
import
myFunc
from
'myFunc'
;
myFunc
();
またはクラス
//------ MyClass.js ------
export
default
class
{
···
}
// no semicolon!
//------ main2.js ------
import
MyClass
from
'MyClass'
;
const
inst
=
new
MyClass
();
関数またはクラス(匿名宣言)をデフォルトでエクスポートする場合、末尾にセミコロンがないことに注意してください。
スクリプト | モジュール | |
---|---|---|
HTML要素 | <script> |
<script type="module"> |
デフォルトモード | 非strict | strict |
トップレベルの変数は | グローバル | モジュールに対してローカル |
トップレベルでのthis の値 |
window |
undefined |
実行 | 同期的に | 非同期的に |
宣言的なインポート(import ステートメント) |
いいえ | はい |
プログラム的なインポート(PromiseベースのAPI) | はい | はい |
ファイル拡張子 | .js |
.js |
for-of
ループ for-of
は、ES6の新しいループで、for-in
と forEach()
の両方を置き換え、新しいイテレーションプロトコルをサポートします。
反復可能なオブジェクト(配列、文字列、マップ、セットなど。チャプター「反復可能オブジェクトとイテレーター」を参照)をループ処理するために使用します
const
iterable
=
[
'a'
,
'b'
];
for
(
const
x
of
iterable
)
{
console
.
log
(
x
);
}
// Output:
// a
// b
break
と continue
は for-of
ループ内で機能します。
for
(
const
x
of
[
'a'
,
''
,
'b'
])
{
if
(
x
.
length
===
0
)
break
;
console
.
log
(
x
);
}
// Output:
// a
配列をループ処理中に、要素とそのインデックスの両方にアクセスします(of
の前の角かっこは、分割代入を使用していることを意味します)。
const
arr
=
[
'a'
,
'b'
];
for
(
const
[
index
,
element
]
of
arr
.
entries
())
{
console
.
log
(
`
${
index
}
.
${
element
}
`
);
}
// Output:
// 0. a
// 1. b
マップ内の [キー、値] エントリをループ処理します(of
の前の角かっこは、分割代入を使用していることを意味します)。
const
map
=
new
Map
([
[
false
,
'no'
],
[
true
,
'yes'
],
]);
for
(
const
[
key
,
value
]
of
map
)
{
console
.
log
(
`
${
key
}
=>
${
value
}
`
);
}
// Output:
// false => no
// true => yes
新しい静的 Array
メソッド
Array.from(arrayLike, mapFunc?, thisArg?)
Array.of(...items)
新しい Array.prototype
メソッド
Array.prototype.entries()
Array.prototype.keys()
Array.prototype.values()
Array.prototype.find(predicate, thisArg?)
Array.prototype.findIndex(predicate, thisArg?)
Array.prototype.copyWithin(target, start, end=this.length)
Array.prototype.fill(value, start=0, end=this.length)
特に、ECMAScript 6では、Map
、WeakMap
、Set
、WeakSet
の4つのデータ構造が新しく追加されました。
マップのキーには任意の値を指定できます
> const map = new Map(); // create an empty Map
> const KEY = {};
> map.set(KEY, 123);
> map.get(KEY)
123
> map.has(KEY)
true
> map.delete(KEY);
true
> map.has(KEY)
false
マップの初期データを設定するために、[キー、値]ペアの配列(または任意の反復可能オブジェクト)を使用できます
const
map
=
new
Map
([
[
1
,
'one'
],
[
2
,
'two'
],
[
3
,
'three'
],
// trailing comma is ignored
]);
セットは一意の要素のコレクションです
const arr = [5, 1, 5, 7, 7, 5];
const unique = [...new Set(arr)]; // [ 5, 1, 7 ]
ご覧のとおり、コンストラクタにこれらの要素に対する反復可能オブジェクト(例の arr
)を渡すと、要素を使用してセットを初期化できます。
WeakMapは、キーがガベージコレクションされるのを妨げないマップです。つまり、メモリリークを心配することなく、オブジェクトにデータを関連付けることができます。例:
//----- Manage listeners
const
_objToListeners
=
new
WeakMap
();
function
addListener
(
obj
,
listener
)
{
if
(
!
_objToListeners
.
has
(
obj
))
{
_objToListeners
.
set
(
obj
,
new
Set
());
}
_objToListeners
.
get
(
obj
).
add
(
listener
);
}
function
triggerListeners
(
obj
)
{
const
listeners
=
_objToListeners
.
get
(
obj
);
if
(
listeners
)
{
for
(
const
listener
of
listeners
)
{
listener
();
}
}
}
//----- Example: attach listeners to an object
const
obj
=
{};
addListener
(
obj
,
()
=>
console
.
log
(
'hello'
));
addListener
(
obj
,
()
=>
console
.
log
(
'world'
));
//----- Example: trigger listeners
triggerListeners
(
obj
);
// Output:
// hello
// world
型付き配列は、バイナリデータを処理するためのECMAScript 6 APIです。
コード例
const
typedArray
=
new
Uint8Array
([
0
,
1
,
2
]);
console
.
log
(
typedArray
.
length
);
// 3
typedArray
[
0
]
=
5
;
const
normalArray
=
[...
typedArray
];
// [5,1,2]
// The elements are stored in typedArray.buffer.
// Get a different view on the same data:
const
dataView
=
new
DataView
(
typedArray
.
buffer
);
console
.
log
(
dataView
.
getUint8
(
0
));
// 5
ArrayBuffer
のインスタンスは、処理されるバイナリデータを格納します。データをアクセスするために2種類のビューが使用されます。
Uint8Array
、Int16Array
、Float32Array
など)は、ArrayBufferを単一型の要素のインデックス付きシーケンスとして解釈します。DataView
のインスタンスを使用すると、ArrayBuffer内の任意のバイトオフセットで、複数の型(Uint8
、Int16
、Float32
など)の要素としてデータにアクセスできます。次のブラウザAPIは型付き配列をサポートしています(詳細については、専用のセクションで説明します)。
ES6 では、データを走査するための新しいメカニズムとしてイテレーションが導入されました。イテレーションには、2 つの中心的な概念があります。
Symbol.iterator
であるメソッドを実装します。そのメソッドは、イテレーターのファクトリーとなります。TypeScript の表記法でインターフェースとして表現すると、これらの役割は次のようになります。
interface
Iterable
{
[
Symbol
.
iterator
]()
:
Iterator
;
}
interface
Iterator
{
next
()
:
IteratorResult
;
}
interface
IteratorResult
{
value
:
any
;
done
:
boolean
;
}
以下の値はイテラブルです。
プレーンオブジェクトはイテラブルではありません(その理由は専用のセクションで説明します)。
イテレーションを通してデータにアクセスする言語構文:
const
[
a
,
b
]
=
new
Set
([
'a'
,
'b'
,
'c'
]);
for-of
ループ
for
(
const
x
of
[
'a'
,
'b'
,
'c'
])
{
console
.
log
(
x
);
}
Array.from()
:
const
arr
=
Array
.
from
(
new
Set
([
'a'
,
'b'
,
'c'
]));
...
)
const
arr
=
[...
new
Set
([
'a'
,
'b'
,
'c'
])];
const
map
=
new
Map
([[
false
,
'no'
],
[
true
,
'yes'
]]);
const
set
=
new
Set
([
'a'
,
'b'
,
'c'
]);
Promise.all()
、Promise.race()
Promise
.
all
(
iterableOverPromises
).
then
(
···
);
Promise
.
race
(
iterableOverPromises
).
then
(
···
);
yield*
:
yield
*
anIterable
;
ジェネレーターは、一時停止と再開が可能なプロセス(コードの一部)と考えることができます。
function
*
genFunc
()
{
// (A)
console
.
log
(
'First'
);
yield
;
console
.
log
(
'Second'
);
}
新しい構文に注目してください:function*
は、ジェネレーター関数のための新しい「キーワード」です(ジェネレーターメソッドもあります)。yield
は、ジェネレーターが自身を一時停止させることができる演算子です。さらに、ジェネレーターは yield
を介して入力を受け取り、出力を送信することもできます。
ジェネレーター関数 genFunc()
を呼び出すと、プロセスを制御するために使用できるジェネレーターオブジェクト genObj
が得られます。
const
genObj
=
genFunc
();
プロセスは、最初は A 行で一時停止しています。genObj.next()
は実行を再開し、genFunc()
内の yield
が実行を一時停止します。
genObj
.
next
();
// Output: First
genObj
.
next
();
// output: Second
ジェネレーターには 4 つの種類があります。
function
*
genFunc
()
{
···
}
const
genObj
=
genFunc
();
const
genFunc
=
function
*
()
{
···
};
const
genObj
=
genFunc
();
const
obj
=
{
*
generatorMethod
()
{
···
}
};
const
genObj
=
obj
.
generatorMethod
();
class
MyClass
{
*
generatorMethod
()
{
···
}
}
const
myInst
=
new
MyClass
();
const
genObj
=
myInst
.
generatorMethod
();
ジェネレーターによって返されるオブジェクトはイテラブルです。各 yield
は、イテレートされた値のシーケンスに寄与します。したがって、ジェネレーターを使用してイテラブルを実装し、それをさまざまな ES6 言語メカニズム (for-of
ループ、スプレッド演算子 (...
) など) で利用できます。
次の関数は、オブジェクトのプロパティを反復処理し、各プロパティごとに [キー、値] のペアを返します。
function
*
objectEntries
(
obj
)
{
const
propKeys
=
Reflect
.
ownKeys
(
obj
);
for
(
const
propKey
of
propKeys
)
{
// `yield` returns a value and then pauses
// the generator. Later, execution continues
// where it was previously paused.
yield
[
propKey
,
obj
[
propKey
]];
}
}
objectEntries()
はこのように使用します。
const
jane
=
{
first
:
'Jane'
,
last
:
'Doe'
};
for
(
const
[
key
,
value
]
of
objectEntries
(
jane
))
{
console
.
log
(
`
${
key
}
:
${
value
}
`
);
}
// Output:
// first: Jane
// last: Doe
objectEntries()
が正確にどのように機能するかは、専用のセクションで説明します。ジェネレーターを使用せずに同じ機能を実装するには、多くの作業が必要です。
ジェネレーターを使用すると、Promise の操作を大幅に簡略化できます。Promise ベースの関数 fetchJson()
と、ジェネレーターを使用してどのように改善できるかを見てみましょう。
function
fetchJson
(
url
)
{
return
fetch
(
url
)
.
then
(
request
=>
request
.
text
())
.
then
(
text
=>
{
return
JSON
.
parse
(
text
);
})
.
catch
(
error
=>
{
console
.
log
(
`ERROR:
${
error
.
stack
}
`
);
});
}
ライブラリ co とジェネレーターを使用すると、この非同期コードは同期的に見えます。
const
fetchJson
=
co
.
wrap
(
function
*
(
url
)
{
try
{
let
request
=
yield
fetch
(
url
);
let
text
=
yield
request
.
text
();
return
JSON
.
parse
(
text
);
}
catch
(
error
)
{
console
.
log
(
`ERROR:
${
error
.
stack
}
`
);
}
});
ECMAScript 2017 では、内部的にジェネレーターに基づいた async 関数が導入されます。async 関数を使用すると、コードは次のようになります。
async
function
fetchJson
(
url
)
{
try
{
let
request
=
await
fetch
(
url
);
let
text
=
await
request
.
text
();
return
JSON
.
parse
(
text
);
}
catch
(
error
)
{
console
.
log
(
`ERROR:
${
error
.
stack
}
`
);
}
}
すべてのバージョンは次のように呼び出すことができます。
fetchJson
(
'http://example.com/some_file.json'
)
.
then
(
obj
=>
console
.
log
(
obj
));
ジェネレーターは、yield
を介して next()
から入力を受け取ることができます。つまり、新しいデータが非同期的に到着するたびにジェネレーターを起動することができ、ジェネレーターにとっては同期的にデータを受け取っているように感じられます。
以下の正規表現機能は、ECMAScript 6 で新たに追加されたものです。
/y
(sticky)は、正規表現の各一致を、前のマッチの末尾に固定します。/u
(unicode) は、サロゲートペア (\uD83D\uDE80
など) をコードポイントとして処理し、正規表現で Unicode コードポイントエスケープ (\u{1F680}
など) を使用できるようにします。flags
は、source
がすでに ES5 でパターンにアクセスできるように、正規表現のフラグへのアクセスを提供します。
> /abc/ig.source // ES5
'abc'
> /abc/ig.flags // ES6
'gi'
RegExp()
を使用して、正規表現のコピーを作成できます。
> new RegExp(/abc/ig).flags
'gi'
> new RegExp(/abc/ig, 'i').flags // change flags
'i'
Promise は、非同期計算の結果を配信するためのコールバックの代替手段です。非同期関数の実装者にはより多くの労力がかかりますが、それらの関数のユーザーにはいくつかの利点があります。
次の関数は、Promise を介して非同期的に結果を返します。
function
asyncFunc
()
{
return
new
Promise
(
function
(
resolve
,
reject
)
{
···
resolve
(
result
);
···
reject
(
error
);
});
}
asyncFunc()
は次のように呼び出します。
asyncFunc
()
.
then
(
result
=>
{
···
})
.
catch
(
error
=>
{
···
});
then()
呼び出しの連鎖 then()
は常に Promise を返すため、メソッド呼び出しを連鎖させることができます。
asyncFunc1
()
.
then
(
result1
=>
{
// Use result1
return
asyncFunction2
();
// (A)
})
.
then
(
result2
=>
{
// (B)
// Use result2
})
.
catch
(
error
=>
{
// Handle errors of asyncFunc1() and asyncFunc2()
});
then()
によって返される Promise P がどのように解決されるかは、そのコールバックが何を行うかによって決まります。
asyncFunction2
の Promise の解決を拾うことができます。さらに、catch()
が 2 つの非同期関数呼び出し (asyncFunction1()
と asyncFunction2()
) のエラーをどのように処理するかに注意してください。つまり、キャッチされないエラーは、エラーハンドラーが存在するまで渡されます。
then()
を介して非同期関数呼び出しを連鎖させると、それらは順番に 1 つずつ実行されます。
asyncFunc1
()
.
then
(()
=>
asyncFunc2
());
そうせずに、すべてをすぐに呼び出すと、それらは基本的に並行して実行されます (Unix プロセスの用語で言うと *fork* )。
asyncFunc1
();
asyncFunc2
();
Promise.all()
を使用すると、すべての結果が揃ったときに通知を受け取ることができます (Unix プロセスの用語で言うと *join* )。その入力は Promise の配列で、その出力は、結果の配列で満たされた単一の Promise です。
Promise
.
all
([
asyncFunc1
(),
asyncFunc2
(),
])
.
then
(([
result1
,
result2
])
=>
{
···
})
.
catch
(
err
=>
{
// Receives first rejection among the Promises
···
});
Promise API は、結果を非同期的に配信するためのものです。Promise オブジェクト (略して Promise) は、そのオブジェクトを介して配信される結果の代わりとなるものです。
状態
状態の変化への反応
then()
で登録するコールバックです。then()
メソッドを持つオブジェクトです。API が解決の通知だけに関心がある場合は常に、thenable のみを要求します (例:then()
および catch()
から返される値。または Promise.all()
および Promise.race()
に渡される値)。状態の変更:Promise の状態を変更するための 2 つの操作があります。どちらかを一度呼び出すと、それ以降の呼び出しは効果がありません。
プロキシを使用すると、オブジェクトに対して実行される操作 (プロパティの取得など) をインターセプトしてカスタマイズできます。これらは、メタプログラミング機能です。
次の例では、proxy
は操作をインターセプトしているオブジェクトであり、handler
はインターセプトを処理するオブジェクトです。この場合、単一の操作 get
(プロパティの取得) のみをインターセプトしています。
const
target
=
{};
const
handler
=
{
get
(
target
,
propKey
,
receiver
)
{
console
.
log
(
'get '
+
propKey
);
return
123
;
}
};
const
proxy
=
new
Proxy
(
target
,
handler
);
プロパティ proxy.foo
を取得すると、ハンドラーはその操作をインターセプトします。
> proxy.foo
get foo
123
インターセプトできる操作のリストについては、完全な API のリファレンスを参照してください。