WeakMapオブジェクトとWeakSetオブジェクトに関して
どうもフロントエンドエンジニアのoreoです。これまでmapオブジェクトとsetオブジェクトについて整理しました。今回は、ほぼ馴染みがなかったWeakMap
オブジェクトとWeakSet
オブジェクトに関して、整理したいと思います。
WeakMapとは?
WeakMap
オブジェクトとは、キーとバリューを保持するコレクションです。Map
オブジェクトに似ていますが、①キーは必ずオブジェクトでなければならない、②キーに指定したオブジェクトの参照は弱参照(後述)となる、③反復処理できないなどの違いがあります。
WeakMapの主なメソッド
-
new WeakMap()
-
WeakMap
オブジェクトを作成できる。
-
-
weakMap.set(key, value)
- 第1引数にキー、第2引数にバリューをそれぞれ渡して設定することができる。
-
weakMap.get(key)
- 引数として渡したキーのバリューを取得できる。
-
weakMap.delete(key)
- 引数として渡したキーのバリューを削除できます。
-
weakMap.has(key)
- 引数として渡したキーが存在するかをbooleanで返します。
const test = {};
const wm = new WeakMap();
wm.set(test, "hoge"); //キー:test、バリュー:"hoge"を設定
console.log(wm.has(test)); //true
console.log(wm.get(test)); //hoge
wm.delete(test); //キー:test、バリュー:"hoge"を削除
console.log(wm.has(test)); //false
弱参照について
そもそもJavaScriptのobject
では、object
の実体への参照がなくなれば、ガベレージコレクションにより、実体が削除され、使用していたメモリが開放されます(ex1参照)。
※ガベージコレクション
オブジェクトや文字列などの実体が、どこからも参照されなくなった場合に、自動的にそれらが使用していたメモリ領域を開放してくれる仕組み。
//ex1
//①objの宣言
//オブジェクトの実体{name:"hoge"}がメモリ上に配置される、objは、その実体への参照を保持している。
let obj = {name:"hoge"};
console.log(obj) //{ name: 'hoge' }
//②objにnullを代入する
//変数obj→実体{name:"hoge"}への参照はなくなる。
obj = null;
//③実体{name:"hoge"}への参照は存在しないので、ガベレージコレクションが実施される。
console.log(obj) //null。
array
などのオブジェクト型を用いて、実体への参照を保持した場合、その参照が保持され続けると、ガベレージコレクションは実施されず、メモリリーク(メモリ領域が解放されず、利用可能なメモリ領域が減少する)が発生する可能性があります(ex2参照)。
//ex2
//①objの宣言
//オブジェクトの実体{name:"hoge"}がメモリ上に配置される、objは、その実体への参照を保持している。
let obj = {name:"hoge"};
//②arrayの宣言
//arrayに、実体{name:"hoge"}への参照が保持される
let array = [obj]
console.log(array) //[ { name: 'hoge' } ]。
//③objにnullを代入する
//obj→実体{name:"hoge"}への参照はなくなる。
obj = null;
//④しかし、array→実体{name:"hoge"}への参照が存在するので、ガベレージコレクションは実施されない。
console.log(array) //[ { name: 'hoge' } ] 。arrayから実体{name:"hoge"}への参照が存在している
一方、WeakMap
オブジェクトのキーに指定したオブジェクトの実体への参照は、その参照以外の参照が存在しない場合、ガベレージコレクションが実施されます。これを、弱参照と言います(ex3)。
//ex3
//①objの宣言
//オブジェクトの実体{name:"hoge"}がメモリ上に配置される、objは、その実体への参照を保持している。
let obj = {name:"hoge"};
//②mapオブジェクトの宣言
//weakMapのキーに実体{name:"hoge"}への弱参照が保持される
let weakMap = new WeakMap();
weakMap.set(obj, "fuga");
console.log(weakMap.has(obj)); //true
//③objにnullを代入する
//obj→実体{name:"hoge"}への参照はなくなる。
obj = null;
//④weakMap→実体{name:"hoge"}への参照は弱参照なので、ガベレージコレクションが実施される。
console.log(weakMap.has(obj)); //false
WeakMapはどんな時に使えるのか?
プライベート変数定義などに使われる模様です。
具体的には、下記のようなDog
クラスを定義すると、変数_name
にクラスの外部からアクセスできます。
//プライベート変数使わない時
class Dog {
constructor(name){
this._name = name;
}
bow(){
console.log(`${this._name}が吠えた`);
}
}
const shiba = new Dog('柴犬');
shiba.bow(); //「柴犬が吠えた」と出力。
console.log(shiba._name) //「柴犬」と出力。外部からアクセスできてしまう。
console.log(shiba) //「Dog { _name: '柴犬' }」と出力。
そこで、WeakMap
を使用すると、変数name
を、プライベート変数にし、外部からアクセスできないようにできます。
const wm = new WeakMap();
class Dog {
constructor(name){
wm.set(this,{name});
}
bow(){
console.log(`${wm.get(this).name}が吠えた`);
}
}
const shiba = new Dog('柴犬');
shiba.bow(); //「柴犬が吠えた」と出力。
console.log(shiba.name) //undefiend。アクセスできない。
console.log(shiba) //「Dog {}」と出力。
WeakSetとは?
WeakSet
オブジェクトとは、重複のない一意のバリューを保持するコレクションです。
Set
オブジェクトに似ていますが、①バリューは必ずオブジェクトでなければならない、②バリューに指定したオブジェクトへの参照は弱参照(詳細は上記参照)となる、③反復処理できないなどの違いがあります。
WeakSetの主なメソッド
-
new WeakSet()
-
WeakSet
オブジェクトを作成できる。
-
-
WeakSet.add(value)
- 引数として渡したバリューを設定することができる。
-
WeakSet.delete(key)
- 引数として渡したリューを削除できます。
-
WeakSet.has(key)
- 引数として渡したキーが存在するかをbooleanで返します。
const test = {};
const ws = new WeakSet();
ws.add(test); //バリューtestを追加
console.log(ws.has(test)); //true
ws.delete(test); //バリューtestを削除
console.log(ws.has(test)); //false
WeakSetはどんな時に使えるのか?
WeakSet
オブジェクトを用いて、参照済みのオブジェクトをチェックすることで、循環参照(オブジェクトの参照がループしてしまう現象)を検知できる模様です。
👇こちら参考
オブジェクトが循環参照しているかを検証する方法(ガチめ) - Qiita
最後に
Vue.jsのソースコードを読んでいた時に、WeakMap
オブジェクトを初めて知り、今回調べてみました。弱参照などは知らなかった概念なので、大変勉強になりました。OSSを読む際に、これらがどのような機能を担っているのか、意識しながら読み、理解を深めて行きたいです!
Discussion