先日、map
とset
について調べていると、WeakMap
WeakSet
というワードが出てきました。
今回はこの耳なじみがないWeakMap
WeakSet
について調べてみました。
WeakMapについて
WeakMapとは
WeakMap
とは何でしょうか。
お馴染みのMDNには下記のように記載されています。
WeakMap オブジェクトは、キーが弱く参照されるキーと値の組のコレクションです。キーはオブジェクトでなければならず、値は任意の値を取ることができます。
「キーが弱く参照される」というのがキーワードですね。
では特徴を見ていきましょう。
WeakMapの特徴
-
キーはオブジェクトのみ
プリミティブ型は使用できません -
キーの弱い参照
WeakMap
内のキーはほかに参照されていない場合、自動的にガーベジコレクションされ、メモリリークを防ぐことができる -
サイズやイテレーション不可
WeakMap
は内容を列挙することができない(for...of
やsize
プロパティを使用できない)
WeakMapの使用例
実際の使用例を見てみましょう
①プライベートデータの隠蔽
オブジェクトキーを持っていないとデータにアクセスすることができません。
これにより、プライベートデータを保存するのに役立ちます。
// WeakMapを使用してオブジェクトのプライベートデータを管理
const privateData = new WeakMap();
class User {
constructor(name) {
// ユーザー情報をプライベートデータとして保存
privateData.set(this, { name });
}
getName() {
// プライベートデータにアクセス
return privateData.get(this).name;
}
}
const user = new User('Alice');
console.log(user.getName()); // "Alice"
// オブジェクトを知っている場合のみアクセス可能
console.log(privateData.get(user)); // { name: "Alice" }
// 直接全体を見る手段がない(列挙不可)
console.log([...privateData]); // TypeError: privateData is not iterable
②DOM要素に関連データを紐づける
DOM要素が削除されると、その要素に紐づけられていたデータもメモリから解放されます。
これによりメモリリークを防止できます。
// DOM要素に関連するデータを管理
const elementData = new WeakMap();
const button = document.createElement('button');
elementData.set(button, { clicked: 0 });
button.addEventListener('click', () => {
// ボタンがクリックされた回数を記録
const data = elementData.get(button);
data.clicked++;
console.log(`Button clicked ${data.clicked} times`);
});
document.body.appendChild(button);
// 5秒後にボタンを削除
// ボタンが削除されると関連データも自動的にガベージコレクションされる
setTimeout(() => {
document.body.removeChild(button);
console.log('Button removed from DOM');
}, 5000);
WeakMapのメリット
WeakMap
には以下のメリットがあるようです。
- 自動的なメモリ解放でメモリリークを防止
- プライベートデータの安全な管理
WeakSetについて
WeakSetとは
MDNには下記のように記載されています。
WeakSet オブジェクトは、コレクションに弱く参照されたオブジェクトを格納することができます。
「弱く参照されたオブジェクト」というのがキーワードですね。
では特徴を見ていきましょう。
WeakSetの特徴
-
オブジェクトのみを格納可能
プリミティブ型は格納できません -
弱い参照を持つ
WeakSet
に追加されたオブジェクトがほかの場所で参照されなくなると、自動的にガーベッジコレクションの対象になります -
イテレーション不可
通常のSet
とは異なり、WeakSet
の内容を列挙したり取得することはできません
基本的に WeakMap
の特徴と同じですね。
WeakSetの使用例
こちらも実際の使用例を見てみましょう
①DOM要素の追跡
WeakSet
を使ってすでに処理済みのDOM要素を追跡します。
ボタンが削除されると、自動的に WeakSet
からも削除されます。
// WeakSetを作成
const processedElements = new WeakSet();
const button1 = document.createElement('button');
const button2 = document.createElement('button');
button1.textContent = 'Button 1';
button2.textContent = 'Button 2';
document.body.appendChild(button1);
document.body.appendChild(button2);
// イベントリスナーで処理済み要素を追跡
button1.addEventListener('click', () => {
if (!processedElements.has(button1)) {
console.log('Button 1 clicked for the first time!');
processedElements.add(button1); // 処理済みに追加
} else {
console.log('Button 1 was already processed.');
}
});
button2.addEventListener('click', () => {
if (!processedElements.has(button2)) {
console.log('Button 2 clicked for the first time!');
processedElements.add(button2);
} else {
console.log('Button 2 was already processed.');
}
});
②オブジェクトの一時的な記録
特定の状態のオブジェクトを一時的に追跡する場合に使用できます。
// WeakSetを作成
const visitedPlayers = new WeakSet();
// プレイヤーオブジェクト
const player1 = { name: 'Kenny' };
const player2 = { name: 'Bob' };
// プレイヤーがエリアを訪れた場合に記録
function visitArea(player) {
if (visitedPlayers.has(player)) {
console.log(`${player.name} has already visited this area.`);
} else {
console.log(`${player.name} visits the area for the first time.`);
visitedPlayers.add(player);
}
}
visitArea(player1); // Kenny visits the area for the first time.
visitArea(player2); // Bob visits the area for the first time.
visitArea(player1); // Kenny has already visited this area.
WeakSetのメリット
WeakSet
には以下のようなメリットがあるようです。
- 自動的なメモリ解放でメモリリークを防止
- データの隠蔽やセキュリティの向上
まとめ
WeakMap
/ WeakSet
どちらも大きなメリットは、
自動的にメモリが解放される点です。
そのため、一時的に使用する情報を保持する際に最も役立つのかなと思いました。
それでは。
参考文献