M.Hiroi's Home Page

JavaScript Programming

お気楽 ECMAScript2015 超入門

[ Home | Light | JavaScript | ES2015 ]

クラス

ES2015 で導入された「クラス (class)」は新しい機能ではなく、プロトタイプベースのオブジェクト指向を使いやすくするための「糖衣構文」です。次の例を見てください。

リスト : 今までのクラス定義

function Foo(a) {
  this.a = a;
}

Foo.prototype.get_a = function() { return this.a; }
Foo.prototype.set_a = function(a) { this.a = a; }
> var obj = new Foo(10)
undefined
> obj.get_a()
10
> obj.set_a(100)
undefined
> obj.get_a(100)
100

このプログラムを ES2015 の class 文を使って書き直すと次のようになります。

リスト : class 文によるクラス定義

class Bar {
  constructor(x) {
    this._a = x;
  }

  get a() { return this._a; }
  set a(x) { this._a = x; }
}
> var obj1 = new Bar(123)
undefined
> obj1.a
123
> obj1.a = 456
456
> obj1.a
456

クラスを定義する場合、従来は function を使っていたので、関数定義と間違える恐れがありましたが、class 文を使うと一目瞭然でクラス定義だとわかります。コンストラクタ (constructor) は初期化を行う特別なメソッドです。複数のコンストラクタを定義することはできません。メソッド定義も ES2015 で導入されたプロパティを省略する書き方をすれば簡単です。もちろん、getter, setter も簡単に定義することができます。

簡単な例として、点を表す Point クラスを示します。

リスト : Point クラス

class Point {
  constructor(x, y) {
    this._x = x;
    this._y = y;
  }
  get x() { return this._x; }
  get y() { return this._y; }

  distance(p) {
    const dx = this.x - p.x;
    const dy = this.y - p.y;
    return Math.sqrt(dx * dx + dy * dy);
  }
}
> var p1 = new Point(0, 0)
undefined
> var p2 = new Point(10, 10)
undefined
> p1.distance(p2)
14.142135623730951

●スタティックメソッド

メソッドの前に static を付けると、スタティックメソッド (クラスメソッド) になります。クラスメソッドは object.method() の形式で呼び出すことはできません。class.method() の形式で呼び出してください。このとき、this の値は class になります。

簡単な例を示しましょう。

リスト : スタティックメソッド

class Baz {
  constructor(x) { this._a = x; }
  get a() { return this._a; }
  set a(x) { this._a = x; }

  static get_baz() { return this.baz ? this.baz : 1; }
  static set_baz(x) { this.baz = x; }
}
> var obj = new Baz(123)
undefined
> obj.get_baz()
TypeError: obj.get_baz is not a function

> Baz.get_baz()
1
> Baz.set_baz(2)
undefined
> Baz.get_baz()
2
> obj
Baz { _a: 123 }
> Baz
{ [Function: Baz] baz: 2 }

class 文の中で変数や定数を定義することはできません。また、prop: value のようにプロパティを定義することもできないようです。クラスメソッドは簡単に定義できますが、クラス変数の定義や初期化は簡単ではないようなので、getter の中でクラス変数が定義されていなければ、初期値を返すようにしてみました。たぶん、もっとよい方法があると思います。ご教示いただけると助かります。

●継承

ES2015 の場合、継承はキーワード extends を使って簡単に行うことができます。

class ClassName extends SuperClassName { ... }

簡単な例を示しましょう。

リスト : 継承

class Foo {
  constructor(a, b) {
    this._a = a;
    this._b = b;
  }
  get a() { return this._a; }
  set a(x) { this._a = x; }
  get b() { return this._b; }
  set b(x) { this._b = x; }
  show(){
    console.log(this._a);
    console.log(this._b);
  }
}

class Bar extends Foo {
  constructor(a, b, c) {
    super(a, b);
    this._c = c;
  }
  get c() { return this._c; }
  set c(x) { return this._c = x; }
  show(){
    super.show()
    console.log(this._c);
  }
}
> var obj = new Foo(1, 2)
undefined
> var obj1 = new Bar(10, 20, 30)
undefined
> obj.a
1
> obj.b
2
> obj1.a
10
> obj1.b
20
> obj1.c
30
> obj.show()
1
2
undefined
> obj1.show()
10
20
30
undefined
> obj
Foo { _a: 1, _b: 2 }
> obj1
Bar { _a: 10, _b: 20, _c: 30 }

スーパークラスのメソッドを呼び出すには super を使います。

  1. super(引数, ...)
  2. super.method_name(引数, ...)

1 はサブクラスのコンストラクタでスーパークラスのコンストラクタを呼び出す場合です。サブクラスのコンストラクタで this を使用する場合は、その前に super() で必ずスーパークラスのコンストラクタを呼び出してください。2 はスーパークラスのメソッドを呼び出す場合です。なお、スタティックメソッドでも super を使用することができます。

●クラス式

クラス式はクラスを定義するもう一つの方法です。

var 変数名 = class [ClassName] [extends SuperClass] { ... }

クラス式はその値を変数に代入しておいて、new 変数名() でオブジェクトを生成することができます。クラス式はクラス名を省略することができます。クラス名を指定した場合、class 文と違って名前の有効範囲はそのクラスの中だけになります。

簡単な実行例を示しましょう。

> var foo = class Foo {}
undefined
> foo
[Function: Foo]
> new foo()
Foo {}
> new Foo()
ReferenceError: Foo is not defined
・・・省略・・・

> var bar = class {}
undefined
> bar
[Function: bar]
> new  bar()
bar {}

class 式を使うと Mix-in も簡単に実現できます。

> var oopsMixin = base => class extends base {
... oops() { console.log("oops"); }
... }
undefined
> var piyopiyoMixin = base => class extends base {
... piyopiyo() { console.log("piyopiyo"); }
... }
undefined
> class Foo {}
[Function: Foo]
> var FooOops = oopsMixin(Foo)
undefined
> var obj = new FooOops()
undefined
> obj.oops()
oops
undefined
> var FooOopsPiyoPiyo = piyopiyoMixin(FooOops)
undefined
> var obj1 = new FooOopsPiyoPiyo()
undefined
> obj1.oops()
oops
undefined
> obj1.piyopiyo()
piyopiyo
undefined

プロミス

●コールバック関数

Web ブラウザなどの GUI アプリケーションは、ユーザーからの入力やシステムの状態変化など、あるイベントをきっかけに処理を行うイベントドリブン型のプログラムです。このようなプログラムは、一般に次のようなメインルーチンを持っています。

  1. 初期化
  2. イベントを取得する
  3. イベントの種類に応じて処理を振り分ける
  4. 2 に戻る

2 から 4 をイベントループと呼び、アプリケーションはユーザーからの入力などのイベントを待ちます。そして、3 の処理に対応する機能を「バインディング (binding)」といいます。バインディングは、ウィンドウでイベントが発生したときに、それに応じて実行するプログラムを設定します。このプログラムを「イベントハンドラ」とか「コールバック関数」と呼びます。

一般に、イベントは非同期に発生するので、それに対応するコールバック関数も非同期に実行されることになります。JavaScript はシングルスレッドのプログラミング言語なので、複数のプログラムを同時に実行することはできません。このため、Web ブラウザや Node.js などでは、イベントが発生したら対応するコールバック関数をすぐに実行するのではなく、いったんキュー (queue, 待ち行列) に登録しておいて、あとでキューからコールバック関数を取り出して順番に処理するようになっています。

また、コールバック関数はイベントだけではなく、時間がかかる処理を実行するときにも使われます。たとえば、ファイル入出力や通信などの処理を終了まで待っていると、他の処理を実行することができなくなります。終了後に行うプログラムをコールバック関数として登録しておけば、終了を待たずに他の処理を実行することができます。

簡単な例を示しましょう。関数 setTimeout() を使うと、指定した時間後に登録した関数を実行することができます。

setTimeout(callback, after [, args, ...])

引数 callback は after msec 後に実行するコールバック関数です。callback に与える引数は after の後ろに指定することができます。

それでは実際に試してみましょう。

> function foo() {
... console.log("foo start");
... setTimeout(console.log, 2000, "oops!!")
... setTimeout(console.log, 1000, "oops!")
... console.log("foo end");
... }
undefined
> foo()
foo start
foo end
undefined
> oops!
oops!!

関数 foo() の実行はすぐに終了しますが、1 秒後と 2 秒後に コールバック関数が実行されるので oops! と oops!! が表示されまます。setTimeout() は after 秒後に callback をキューに登録する処理を行います。foo() の処理を終了すると REPL に戻りますが、そこでキューの状態を監視していて、キューに callback が登録されているならば、それを順番に取り出して実行します。

setTimeout() のほかにも、setInterval() や setImmediate() があります。

setInterval(callback, msec [, args, ...])
setImmediate(callback [, args, ...])

setInterval() は callback を msec 間隔で実行します。setImmediate() は callback をすぐにキューに登録します。なお、これらの関数は Node.js ではグローバルに定義されていますが、Web ブラウザの JavaScript では window オブジェクトのメソッドになっています。名前の前に window. を付けてください。

簡単な例を示しましょう。

リスト : setImmediate() の実行例

function foo(n) {
  if (n > 0) {
    console.log("foo" + n);
    setImmediate(foo, n - 1);
  }
}

function bar(n) {
  if (n > 0) {
    console.log("bar" + n);
    setImmediate(bar, n - 1);
  }
}

foo(10);
bar(8);
C>node test.js
foo10
bar8
foo9
bar7
foo8
bar6
foo7
bar5
foo6
bar4
foo5
bar3
foo4
bar2
foo3
bar1
foo2
foo1

関数 foo() は "foo" + 引数 を n 回、bar() は "bar" + 引数 を n 回表示します。どちらの関数も setImmediate() で foo(n - 1), bar(n - 1) をコールバック関数として登録します。最初に foo(10), bar(8) を実行すると、キューには foo(9), bar(7) が登録されます。次に、キューから foo(9), bar(7) を取り出して実行すると、キューには foo(8), bar(6) が登録されます。このように、コールバック関数をキューに登録することで、n 回の繰り返しを相互に行わせることができます。

●コールバック関数の同期処理

個々のコールバック関数は非同期に実行されるため、それを順番に実行 (同期処理) するのはけっこう面倒です。たとえば、foo(callback_a); bar(callback_b); baz(callback_c); と順番に並べても、コールバック関数が a, b, c の順番で実行されるとは限りません。次の例を見てください。

リスト : コールバック関数の同期処理 (間違い版)

function foo(func) {
  setTimeout(func, 3000);
}

function bar(func) {
  setTimeout(func, 1000);
}

function baz(func) {
  setTimeout(func, 2000);
}

function callback_a() {
  console.log("callback_a");
}

function callback_b() {
  console.log("callback_b");
}

function callback_c() {
  console.log("callback_c");
}

foo(callback_a);
bar(callback_b);
baz(callback_c);
C>node test1.js
callback_b
callback_c
callback_a

このように、コールバック関数の起動タイミングは foo(), bar(), baz() で異なるため、単純に foo(), bar(), baz() を並べただけでは、コールバック関数の同期処理を行うことはできません。

このような場合、コールバック関数を入れ子にするとうまくいきます。

リスト : コールバック関数の同期処理 (入れ子バージョン)

foo(() => {
  callback_a();
  bar(() => {
    callback_b();
    baz(() => {
      callback_c();
    });
  });
});
C>node test2.js
callback_a
callback_b
callback_c

foo() に渡すアロー関数の中で callback_a() を実行し、それが終了してから bar() を呼び出します。次に、bar() に渡すアロー関数の中で callback_b() を実行し、それが終了したら baz() を呼び出します。最後に、bar() に渡すアロー関数の中で callback_c() を実行します。これで、コールバック関数を a, b, c の順番で実行することができます。

ただし、この方法は同期させるコールバック関数の数に比例して入れ子が深くなるので、プログラムを記述するのが難しくなり、可読性も低下するという欠点があります。このような場合、ES2015 で導入された「プロミス (Promise)」を使うとプログラムをすっきり記述することができます。

●プロミス

「プロミス (promise)」は、まだ計算を完了してはいないが、いずれ計算が完了することを表すオブジェクトです。

new Promise(function) => promise_object  (pending 状態)

Promise() には関数 function を渡します。Promise() はまだ計算を完了していない promise_object を返します。これを pending 状態といいます。引数 function の仕様を示します。

function(resolve, reject) { 処理; ... }
成功ならば resolve() を呼ぶ (fulfilled 状態)
失敗ならば reject() を呼ぶ  (rejected 状態)

引数 resolve, reject は関数で、処理の中で resolve() を呼ぶと計算が完了したことになります。これを fulfilled 状態といいます。このとき、resolve() の引数に計算結果を渡します。reject() を呼ぶと計算が失敗したことになります。これを rejected 状態といいます。reject() の引数にはエラーの情報を渡します。

Promise() を実行すると、引数の function はすぐに実行されることに注意してください。実際に非同期処理を行うには、function の中で非同期処理を行う関数、たとえば setTimeout() などを実行する必要があります。それに渡すコールバック関数の中で resolve() や reject() を呼び出します。

promise_object の値を求めるにはメソッド then(), catch() を使います。

then(onFulfilled [, onRejected])
catch(onRejected)

引数 onFulfilled は fulfilled 状態のときに実行される関数、onRejected は rejected 状態のときに実行される関数です。どちらの関数も引数はひとつで、resolve() または reject() の引数がそのまま渡されます。promise_object が pending 状態の場合、then() や catch() の実行は遅延されます。

then() と catch() は promise_object を返します。コールバック関数で promise_object 以外の値を返した場合、その値をラップした promise_object が返されます。

簡単な実行例を示しましょう。

> function twice(x) {
... console.log(x);
... return x * 2;
... }
undefined
> Promise.resolve(1).then(twice).then(twice).then(twice).then(twice)
Promise { <pending> }
> 1
2
4
8

関数 twice() は引数を表示して、それを 2 倍した値を返します。Promise.resolve(x) は値が x に確定した promise_object を返します。then() は promise_object を返すので、ドット ( . ) で連結していくことができます。then(twice) を連結していくことで、値が倍々されていきます。

then() で onRejected を指定していない場合、rejected 状態の promise_object はスルーされます。次の例を見てください。

> Promise.reject(0).then(twice).then(twice).then(twice).catch(console.log)
Promise { <pending> }
> 0
> Promise.reject(0).then(twice).then(twice).then(twice, console.log)
Promise { <pending> }
> 0

Promise.reject() は rejected 状態の promise_object を返します。引数は reject() と同じくエラーを表す情報です。最初の例では、then() に onRejected が指定されていないので全てスルーされ、最後の catch() でエラーが捕捉されます。次の例では、最後の then() で onRejected が指定されているので、そこでエラーが捕捉されます。

コールバック関数の同期処理をプロミスを使って書き直すと次のようになります。

リスト : コールバック関数の同期処理 (プロミスバージョン)

function callback_a() {
  console.log("callback_a");
}

function callback_b() {
  console.log("callback_b");
}

function callback_c() {
  console.log("callback_c");
}

function makePromise(func, d) {
  return new Promise((resolve, reject) => {
    setTimeout(() => { func(); resolve(d); }, d);
  });
}

makePromise(callback_a, 3000)
.then(x => makePromise(callback_b, 1000))
.then(x => makePromise(callback_c, 2000));
C>node test3.js
callback_a
callback_b
callback_c

関数 makePromise() はプロミスを生成します。引数のアロー関数の中で setTimeout() を呼び出し、そのコールバック関数の中で func() と resolve() を呼び出します。これで、引数の d msec 後に関数 func を実行し、プロミスを fulfilled 状態に移行します。

あとは、makePromise() によるプロミスの生成を then() でつなげばいいわけです。then() に渡すアロー関数の引数 x にはプロミスの値が渡されます。これでコールバック関数 a, b, c を順番に実行することができます。

●Promise.all() と Promise.race()

Promise.all(iterable) は引数 iterable の要素 (プロミス) がすべて成功したとき、Promise.all() も成功します。ひとつでも失敗すると、Promise.all() も失敗します。成功した場合、プロミスの値を格納した配列が Promise.all() の値になります。

簡単な実行例を示します。

> Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]).then(console.log)
Promise { <pending> }
> [ 1, 2, 3 ]
> Promise.all([Promise.resolve(1), Promise.reject("oops"), Promise.resolve(3)]).then(console.log, console.log)
Promise { <pending> }
> oops

Promise.race(iterable) は引数 iterable の要素 (プロミス) で最初に完了したプロミスの値で成功・失敗が決まります。

> Promise.race([makePromise(callback_a, 3000), makePromise(callback_b, 1000)]).then(console.log)
Promise { <pending> }
> callback_b
1000
callback_a
> Promise.race([makePromise(callback_a, 3000), Promise.reject("oops")
,makePromise(callback_b, 1000)]).then(console.log, console.log)
Promise { <pending> }
> oops
callback_b
callback_a

Promise.race() の値が決定したとき、実行中のコールバック関数はそのまま最後まで実行されます。途中で停止しないことに注意してください。


プロキシ

「プロキシ (Proxy)」は、ターゲットとなるオブジェクトの基本操作をトラップして、その動作をカスタマイズするために使用します。

new Proxy(target, handler) => Proxy_object

引数 target は操作をトラップするオブジェクト、handler はトラップしたメソッドを含むオブジェクトです。トラップできる基本操作と、それに対応するメソッドについては、リファレンスマニュアル Proxy - JavaScript | MDNProxy オブジェクト (JavaScript) をお読みください。

簡単な例として、プロパティのアクセスをトラップしてみましょう。プロパティの参照はメソッド get() で、更新はメソッド set() でトラップすることができます。

get(target, prop, receiver)
set(target, prop, value, receiver)

target はターゲットのオブジェクト、prop はプロパティ名、receiver はプロキシかそれを継承するオブジェクトです。引数 receiver は省略しても動作します。set() の引数 value はプロパティに書き込む値です。

リスト : Proxy の使用例

var obj = {a: 1, b: 2};
var handler = {
  get(target, name) {
    console.log("get " + name);
    return target[name];
  },
  set(target, name, value) {
    console.log("set " + name);
    target[name] = value;
  }
}

var p = new Proxy(obj, handler);
console.log(p.a);
console.log(p.b);
p.a = 10;
p.b = 20;
console.log(obj)

変数 handler にメソッド get(), set() を格納したオブジェクトをセットします。トラップされるのは get() と set() だけで、他の基本操作はトラップされません。get() と set() はメッセージを表示したあと、本来の操作を行っています。

そして、new Proxy(obj, handler) とすると、obj の操作をトラップする Proxy オブジェクト p を生成することができます。あとは、p.a, p.b とすると、トラップした get() が、p.a = 10, p.b = 20 とすると set() が実行されます。

それでは実行してみましょう。

C>node test4.js
get a
1
get b
2
set a
set b
{ a: 10, b: 20 }

このように、Proxy オブジェクトを介して他のオブジェクトの操作をトラップすることができます。

●Reflect

なお、オブジェクトの基本操作はクラス Reflect のスタティックメソッドとして定義されています。たとえば、プロパティをアクセスする基本操作は Reflect.get() と Reflect.set() になります。簡単な例を示しましょう。

> var obj = {x: 123}
undefined
> Reflect.get(obj, "x")
123
> Reflect.set(obj, "x", 456)
true
> obj
{ x: 456 }

詳しい説明はリファレンスマニュアル Reflect - JavaScript | MDN をお読みください。


セット (Set)

「セット (Set)」は集合を表すコレクションです。Set は任意のデータ型を格納することができますが、同じ値のデータは一つだけしか格納できません。同値性 (等価性) については、等価性の比較とその使いどころ - JavaScript | MDN をお読みくださいませ。

new Set([iterable]) => Set_object

new Set() は空のセットを生成します。iterable オブジェクトが渡された場合、その要素がセットに追加されます。セットの要素数はプロパティ size に格納されています。

主なインスタンスメソッドを下表に示します。

表 : Set のインスタンスメソッド
メソッド機能
add(item)セットに item を追加する
clear()セットを空にする
delete(item)セットから item を削除する
forEach(func)セットの要素に関数 func を適用する
has(item)セットに item が含まれていれば真を返す
[@@iterator]()要素を順番に取り出すイテレータ
keys()同上
values()同上
entries()要素を配列 [item, item] に格納して取り出すイテレータ

セットの forEach() とイテレータは挿入された順番で要素にアクセスします。

簡単な使用例を示します。

> s = new Set()
Set {}
> s.add(3)
Set { 3 }
> s.add(2)
Set { 3, 2 }
> s.add(1)
Set { 3, 2, 1 }
> s.has(2)
true
> s.has(4)
false
> s.size
3
> for (let x of s) console.log(x)
3
2
1
undefined
> s.delete(2)
true
> s.has(2)
false
> s
Set { 3, 1 }
> s.add(1)
Set { 3, 1 }
> s.add(2)
Set { 3, 1, 2 }

forEach(func) の引数 func には、3 つの引数が渡されます。

func(value, key, obj)
value : コレクションの値
key   : コレクションのキー
obj   : コレクション

セットの場合、value と key は同じになります。簡単な例を示します。

> var s = new Set([1,2,3,4,5])
undefined
> s.forEach((...args) => console.log(args))
[ 1, 1, Set { 1, 2, 3, 4, 5 } ]
[ 2, 2, Set { 1, 2, 3, 4, 5 } ]
[ 3, 3, Set { 1, 2, 3, 4, 5 } ]
[ 4, 4, Set { 1, 2, 3, 4, 5 } ]
[ 5, 5, Set { 1, 2, 3, 4, 5 } ]
undefined

values(), keys(), entries() はイテレータを返します。セットの場合、values() と keye() は同じになります。また、entries() はキーと値を配列に格納して返しますが、セットの場合、配列の要素は同じ値になります。

> for (let x of s.values()) console.log(x)
1
2
3
4
5
undefined
> for (let x of s.keys()) console.log(x)
1
2
3
4
5
undefined
> for (let x of s.entries()) console.log(x)
[ 1, 1 ]
[ 2, 2 ]
[ 3, 3 ]
[ 4, 4 ]
[ 5, 5 ]
undefined

マップ (Map)

一般に、キーと値を関連付けて格納するデータ構造を「連想配列」といいます。連想配列は多くのプログラミング言語でサポートされていて、ハッシュ (hash), 辞書 (dictonary), マップ (map) などと呼ばれています。JavaScript の場合、連想配列はプロパティをキーとするオブジェクトで代用することができますが、ES2015 では新たなコレクションとして「マップ (Map)」が導入されました。

マップの場合、キーと値は任意のデータ型を使うことができます。キーの同値性 (等価性) については、等価性の比較とその使いどころ - JavaScript | MDN をお読みくださいませ。

new Map([iterable]) => Map_object

new Map() は空のマップを生成します。iterable オブジェクトが渡された場合、その要素 (キーと値を組にした配列) がマップに追加されます。マップの要素数はプロパティ size に格納されています。

主なインスタンスメソッドを下表に示します。

表 : Map のインスタンスメソッド
メソッド機能
set(key, value)マップに key と value を追加する
get(key)マップから key の値を求める
delete(key)マップから key とその値を削除する
clear()マップを空にする
forEach(func)マップの要素に関数 func を適用する
has(key)マップに key が含まれていれば真を返す
[@@iterator]()要素を配列 [key, value] に格納して取り出すイテレータ
entries()同上
keys()キーを取り出すイテレータ
values()値を取り出すイテレータ

マップの forEach() とイテレータは挿入された順番で要素にアクセスします。

簡単な使用例を示します。

> var m = new Map()
undefined
> m
Map {}
> m.set("foo", 10)
Map { 'foo' => 10 }
> m.set("bar", 20)
Map { 'foo' => 10, 'bar' => 20 }
> m.set("baz", 30)
Map { 'foo' => 10, 'bar' => 20, 'baz' => 30 }
> m.get("foo")
10
> m.get("oops")
undefined
> m.has("bar")
true
> m.delete("bar")
true
> m.has("bar")
false
> m.size
2
> for (let [k, v] of m) console.log(k, v)
foo 10
baz 30
undefined

forEach(func) の引数 func には、3 つの引数が渡されます。

func(value, key, obj)
value : コレクションの値
key   : コレクションのキー
obj   : コレクション

簡単な例を示します。

> var m = new Map([["foo", 10], ["bar", 20], ["baz", 30]])
undefined
> m
Map { 'foo' => 10, 'bar' => 20, 'baz' => 30 }
> m.forEach((...args) => console.log(args))
[ 10, 'foo', Map { 'foo' => 10, 'bar' => 20, 'baz' => 30 } ]
[ 20, 'bar', Map { 'foo' => 10, 'bar' => 20, 'baz' => 30 } ]
[ 30, 'baz', Map { 'foo' => 10, 'bar' => 20, 'baz' => 30 } ]
undefined

values(), keys(), entries() はイテレータを返します。

> for (let x of m.keys()) console.log(x)
foo
bar
baz
undefined
> for (let x of m.values()) console.log(x)
10
20
30
undefined
> for (let x of m.entries()) console.log(x)
[ 'foo', 10 ]
[ 'bar', 20 ]
[ 'baz', 30 ]
undefined

Copyright (C) 2017 Makoto Hiroi
All rights reserved.

[ Home | Light | JavaScript | ES2015 ]