<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>WeakMap</title><link>https://blog.ast.moe/tags/weakmap/</link><description>Recent posts from 五里霧中</description><generator>Hugo</generator><item><title>WeakMapとは何か</title><link>https://blog.ast.moe/blog/2026-10-08/</link><pubDate>Thu, 08 Oct 2026 21:50:47 +0900</pubDate><guid>https://blog.ast.moe/blog/2026-10-08/</guid><description>&lt;p&gt;JavaScriptには、キーと値を組にして保存する&lt;code&gt;Map&lt;/code&gt;と、似た名前の&lt;code&gt;WeakMap&lt;/code&gt;があります。どちらも&lt;code&gt;set()&lt;/code&gt;で登録して、&lt;code&gt;get()&lt;/code&gt;で取り出せます。ただ、WeakMapには&lt;code&gt;size&lt;/code&gt;も&lt;code&gt;keys()&lt;/code&gt;もありません。&lt;/p&gt;
&lt;p&gt;WeakMapは、オブジェクトに情報を対応付けつつ、その登録だけを理由にオブジェクトをメモリへ残し続けないための仕組みです。「弱い」という名前が指しているのは、データの信頼性や検索の精度ではなく、キーを生かす参照の扱いです。&lt;/p&gt;
&lt;p&gt;まずは使う側から見ていきます。GC内部での処理と実際の不具合は、続きの&lt;a href="https://blog.ast.moe/blog/2026-10-08-2/"&gt;WeakMapと世代別GC&lt;/a&gt;で扱います。&lt;/p&gt;
&lt;h2 id="オブジェクトに情報を付ける"&gt;オブジェクトに情報を付ける&lt;/h2&gt;
&lt;p&gt;たとえば、あるオブジェクトが何回使われたかを、オブジェクト本体へプロパティを追加せずに記録したいとします。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;counts&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;WeakMap&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;item&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;button&amp;#34;&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;counts&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;item&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;counts&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;item&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;counts&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;item&lt;/span&gt;) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;counts&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;item&lt;/span&gt;)); &lt;span style="color:#75715e"&gt;// 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;item&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// { name: &amp;#34;button&amp;#34; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;item&lt;/code&gt;をキーにして、回数を別の場所に保存しています。オブジェクト自体を書き換えないので、元からあるプロパティとの名前の衝突を避けられます。凍結されたオブジェクトにも、外側から情報を対応付けられます。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;item&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Object.&lt;span style="color:#a6e22e"&gt;freeze&lt;/span&gt;({ &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;button&amp;#34;&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;descriptions&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;WeakMap&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;descriptions&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;item&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;画面の送信ボタン&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;descriptions&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;item&lt;/span&gt;)); &lt;span style="color:#75715e"&gt;// 画面の送信ボタン
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ここで凍結しているのは&lt;code&gt;item&lt;/code&gt;です。WeakMapへの登録は&lt;code&gt;item&lt;/code&gt;のプロパティを変更していません。&lt;/p&gt;
&lt;p&gt;基本操作は&lt;code&gt;set()&lt;/code&gt;、&lt;code&gt;get()&lt;/code&gt;、&lt;code&gt;has()&lt;/code&gt;、&lt;code&gt;delete()&lt;/code&gt;です。登録していないキーの&lt;code&gt;get()&lt;/code&gt;は&lt;code&gt;undefined&lt;/code&gt;を返します。値として&lt;code&gt;undefined&lt;/code&gt;を登録することもできるので、その場合に登録の有無を判定するには&lt;code&gt;has()&lt;/code&gt;を使います。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;WeakMap&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;undefined&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;)); &lt;span style="color:#75715e"&gt;// undefined
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;has&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;)); &lt;span style="color:#75715e"&gt;// true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#66d9ef"&gt;delete&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;)); &lt;span style="color:#75715e"&gt;// true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;has&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;)); &lt;span style="color:#75715e"&gt;// false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;これらのメソッドの振る舞いは、&lt;a href="https://tc39.es/ecma262/multipage/keyed-collections.html#sec-properties-of-the-weakmap-prototype-object"&gt;ECMAScriptのWeakMapの定義&lt;/a&gt;で確認できます。&lt;/p&gt;
&lt;h2 id="同じ内容でも別のキー"&gt;同じ内容でも別のキー&lt;/h2&gt;
&lt;p&gt;WeakMapはオブジェクトの内容を比較して検索するものではありません。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;WeakMap&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;登録済み&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;)); &lt;span style="color:#75715e"&gt;// 登録済み
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;)); &lt;span style="color:#75715e"&gt;// undefined
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;({ &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; })); &lt;span style="color:#75715e"&gt;// undefined
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;a&lt;/code&gt;と&lt;code&gt;b&lt;/code&gt;は、どちらも&lt;code&gt;id: 1&lt;/code&gt;という内容ですが、別のオブジェクトです。コピーを作っても元のキーにはなりません。逆に、同じオブジェクトのプロパティを変更しても、そのオブジェクトをキーにした登録は引き続き取り出せます。&lt;/p&gt;
&lt;p&gt;IDの数値や名前の文字列で検索したい場合は、Mapを使う方が自然です。WeakMapのキーに文字列や数値を&lt;code&gt;set()&lt;/code&gt;すると&lt;code&gt;TypeError&lt;/code&gt;になります。&lt;/p&gt;
&lt;p&gt;現在の仕様では、オブジェクトに加えて、&lt;code&gt;Symbol.for()&lt;/code&gt;で共有登録されていないSymbolもキーにできます。&lt;code&gt;Symbol(&amp;quot;id&amp;quot;)&lt;/code&gt;は使えますが、&lt;code&gt;Symbol.for(&amp;quot;id&amp;quot;)&lt;/code&gt;は使えません。この区別は&lt;a href="https://tc39.es/ecma262/multipage/abstract-operations.html#sec-canbeheldweakly"&gt;CanBeHeldWeakly&lt;/a&gt;で定義されています。この記事の例では、用途が分かりやすいオブジェクトを使います。&lt;/p&gt;
&lt;h2 id="mapに残る参照"&gt;Mapに残る参照&lt;/h2&gt;
&lt;p&gt;通常のMapにも、オブジェクトをキーとして登録できます。&lt;/p&gt;</description></item></channel></rss>