こんにちは、ほそ道です。
今回はES6のシンタックスではなくオブジェクトについて、
今のところ使い出あるかなー、知っといたほうがいいかなーというものを備忘録的にまとめてみました。
一つ一つはもっと掘り下げ甲斐のあるものもあるので、細かい挙動を追う記事を別途書くかもしれません。
実行方法
実行環境準備
簡単に実行環境から紹介します。
今回はscript.jsというファイルに書いたES6コードを実行していきましょう。
まずはプロジェクトディレクトリを作って、コマンドを実行します。
コンソール
npm init
npm i -D babel
npm i -D babel-runtime
touch script.js
script.jsの実行
例としてscript.jsを下記のようにしてみます。
script.js
console.log(Symbol());
script.jsに処理を記述したら下記のコマンドで動かします。
コンソール
babel --optional runtime script.js | iojs
※iojsはnodeでも良いですが、どこかしら実行結果が変わる可能性はあります(バージョンも絡みますのでその辺はよしなにしてくださいませ)
※オブジェクトが無いと言われてしまう場合、JSソースの頭にrequire('babel/polyfill');
をつけてみてください。
Symbol
Symbol型
- ES6で登場した新しい型。
- 各
Symbol()
はユニークとなる。 - 引数を与えることができるがSymbolの意味を表すもので、デバッグなどで使われる。処理上で意味を持たせることはできない。
Symbolその1
const s = Symbol();
console.log(typeof s); // symbol
console.log(Symbol('desc') === Symbol('desc')); // false
console.log(Symbol('desc')); // Symbol(desc)
- Symbolはオブジェクトのキーにできる
- Symbolを持っていないスコープはそのプロパティにはアクセスできないので、値をスコープ内で隠匿できる。
- ただし
Object.getOwnPropertySymbols
やReflect.ownKeys
を使うとプロパティに使用しているシンボルを取得できてしまう。
Symbolその2
const o = {val: 100};
(() => {
const s = Symbol('hundred');
o[s] = 100;
console.log(o[s]);
})();
// ここでは基本的にsは参照できない
for (let p in o) {
console.log(p); // valしか出てこない
}
// 抜け道的にSymbol取得
console.log(Object.getOwnPropertySymbols(o)[0]); // Symbol(hundred)
console.log(Reflect.ownKeys(o)); // [ 'val', Symbol(hundred) ]
- Symbolはユニークなのでオブジェクトのプロパティとした時、当然プロパティもユニークなものとなる。
Symbolその3-1
const o = {},
a = Symbol('xxx'),
b = Symbol('xxx');
o[a] = 100;
o[b] = 200;
console.log(o[b]); // 200
console.log(o[a]); // 100(上書きされない)
-
for
メソッドを使うことでもSymbolを生成できる。その場合、引数はシンボルにアクセスするためのキーとしての意味合いを持つ。 - キーは
keyFor
メソッドで取り出すこともできる。
Symbolその3-2
const o = {},
a = Symbol.for('xxx'),
b = Symbol.for('xxx');
console.log(a === b); // true
console.log(a); // Symbol(xxx)
o[a] = 100;
o[b] = 200;
console.log(o[b]); // 200
console.log(o[a]); // 200(上書きされる)
console.log(Symbol.keyFor(a)); // xxx
Symbol.iterator
- Symbol.iteratorは組み込みのシンボルで、ES6で定義されたIterableとIteratorというインターフェースを実現する
- IterableはIteratorを返す
[Symbol.iterator]
メソッドを実装するインターフェースで、for..of
が使える - Iteratorは
next
メソッドを実装するインターフェース
Iterator
const iterableImpl = {
vals: {p: 1, q: 2, r: 3},
[Symbol.iterator]() {
const self = this,
keys = Object.keys(self.vals);
return {
next: () => {
const nextKey = keys.shift();
return {value: self.vals[nextKey], done: !nextKey};
}
}
}
};
const itratorImpl = iterableImpl[Symbol.iterator]();
console.log(itratorImpl.next()); // { value: 1, done: false }
console.log(itratorImpl.next()); // { value: 2, done: false }
console.log(itratorImpl.next()); // { value: 3, done: false }
console.log(itratorImpl.next()); // { value: undefined, done: true }
for (let e of iterableImpl) {
console.log(e); // "1" "2" "3"
}
コレクション
WeakMap
- key-valueの形で要素を保持する
- キーはオブジェクトでなければならない
- 弱い参照を持ち、キーが他のどこからも参照されない場合、キーはGCの対象になる
- 反復処理はできない
WeakMap
class Hoge {}
const a = new Hoge, b = new Hoge, c = new Hoge;
const wm = new WeakMap([[a, 100], [b, 200]]);
wm.set(b, 300);
console.log(wm.get(a)); // 100
console.log(wm.get(b)); // 300
console.log(wm.get(c)); // undefined
wm.set({}, 300); // {}はGCの対象になる
Map
-
WeakMapとの比較
- オブジェクト以外の値(Number, Bool, String)もキーになる
-
forEach
,keys
,for..of
などの反復処理を実現する機能を持つ - 強い参照を持ち、キーとなったオブジェクトはGC対象にならない(メモリを食う)
-
一般的なオブジェクトとの比較
- 数値やオブジェクトをキーにすることが容易
- サイズを得ることが容易
Map
class Hoge {}
const a = new Hoge;
const m = new Map([[1, "x"], [2, "y"]]);
m.set(3, "z");
m.set(a, "hoge");
console.log(m.get(2)); // y
console.log(m.get(3)); // z
console.log(m.get(a)); // hoge
m.forEach((v, k) => {
console.log(`${k} -> ${v}`); //"1 -> x" "2 -> y" "3 -> z" "[object Object] -> hoge"
});
for (let e of m) {
console.log(e); // "[ 1, 'x' ]" "[ 2, 'y' ]" "[ 3, 'z' ]" "[ {}, 'hoge' ]"
}
m.set({}, "fuga"); // WeakSetと異なり、{}はGCに回収されない
console.log(m.size); // 5
WeakSet
- Mapと違って一意なオブジェクトを保持する。
- 格納できるのはオブジェクトのみ。
- 弱い参照を持ち、要素が他のどこからも参照されない場合、要素はGCの対象になる
- 要素の取り出しができないので状態管理あたりが用途になるかと思います。例えば一度使ったものはココに入れ、使用済みかどうかのチェックに使うなど。
WeakSet
const a = {v: 1}, b = {v: 2}, c = {v: 3};
const ws = new WeakSet([a, b]);
ws.add(c);
console.log(ws.has(a)); // true
console.log(ws.has(b)); // true
ws.delete(a);
console.log(ws.has(a)); // false
ws.add({}); // {}はGCの対象になる
Set
- WeakSet-Setの関係がWeakMap-Mapの関係とほぼ同じ
Set
const s = new Set([{v: 1}, true, 1, 'moji']);
s.add({v: 2});
s.forEach(v => {
console.log(v); // "{ v: 1 }" "true" "1" "moji" "{ v: 2 }"
});
for (let e of s) {
console.log(e); // "{ v: 1 }" "true" "1" "moji" "{ v: 2 }"
}
for (let e of s.values()) {
console.log(e); // "{ v: 1 }" "true" "1" "moji" "{ v: 2 }"
}
console.log(s.delete(1)); // true
console.log(s.delete(2)); // false
console.log(s.size); // 4
Promise
- 処理結果が期待値を返すかどうか不明な処理の呼び出しにおいて、成功時/失敗時それぞれのコールバックを定義できる。
- サーバ通信などの非同期処理で用いるイメージ。
Promise
const getEven = new Promise((resolve, reject) => {
const even = Math.floor(Math.random() * 10);
if (even % 2 === 0) {
resolve(even);
} else {
reject(even);
}
});
Promise.all(getEven.then(result =>{
console.log(`getting even successful: ${result}`);
}).catch(reject => {
console.log(`value error: ${reject}`);
}));
Object.assign
これだけ既存オブジェクトのメソッドになっちゃいますが使い出ありそうだったので。
- 第一引数のオブジェクトに第二引数のオブジェクトが持つメンバを割り当てる
Object.assign
class Man {
constructor(name, age) {
Object.assign(this, {
name: name,
age: age,
greet: () => `Hello I'm ${this.name}, I'm ${this.age} years old`
})
}
}
const hide = new Man('Hide', 30);
console.log(hide.greet()); // Hello I'm Hide, I'm 30 years old
今回は以上です。