質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

2002閲覧

WeakMapの使い道が分かりません。

murabito

総合スコア108

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

2グッド

8クリップ

投稿2018/04/15 08:15

編集2018/04/16 13:19

★ 分からないこと・知りたいこと

  1. WeakMapの使い道が分からなかったり、これを使うと何が良いのかが分かりません。

そこをクリアにしたいです。

  1. MapにしろWeakMapにしろ、キーにオブジェクトをなぜ使う必要があるのかが分からない。(ということがよく分かっていないのだと後になって気づきました)

★ 試したこと

1.WeakMapについて以下の記事を呼んで、一応理解はした気はします。ただ、使い道やメリットがイメージ出来ていません。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/WeakMap

https://uhyohyo.net/javascript/16_1.html

2. MapとWeakMapの動作の違いをコードを打って確かめました。

Map

let x = { id: 1 } let y = { id: 5 }; const map = new Map([[x, 100], [y, 5]]); //console.log(map.get(obj)) // 100 x = {}; console.log(map.get(x)) // undefined console.log(map.has(x)); // false for (var i of map.keys()) { console.log(i) } // { id: 1 } // これが残るのがMap // { id: 5 }

WeakMap

'use strict'; let x = { id: 1 } let y = { id: 5 }; const weakMap = new WeakMap([[x, 100]]); console.log(weakMap.get(x)) // 100 x = {}; console.log(weakMap.has(x)); // false console.log(weakMap.get(x)) // undefined //weakMapにはそもそもkeys()メソッドはない。

Object

let x = { y: { id: 1 } } console.log(Object.keys(x)); // ['y'] console.log(x.hasOwnProperty('y')); // true delete x.y; console.log(Object.keys(x)); // [] console.log(x.hasOwnProperty('y')); // false

★ 分かってること・分かったこと

  • WeakMapのキー以外から参照されていないものはガベージコレクションの対象になる。(WeakMapは参照カウントを増やさないみたいなイメージ)

★ 分かってるようで分かっていないこと

MapにしろWeakMapにしろ、キーにオブジェクトをなぜ使う必要があるのかが分からない。

と、冒頭の「分からないこと・知りたいこと」に追加しましたが、キーをオブジェクトにするということは、リレーショナルデータベースみたいなのをイメージすれば良いのかなと思ったのですが、こういう捉え方はどうなのか。。。

let authorA = { id: 1, name: 'xxx' } let articles = [ {id: 1, title: 'aaa'}, {id: 2, title: 'bbb'} ] let wmap = new WeakMap(); wmap.set(authorA, articles); console.log(wmap.get(authorA)); // [ { id: 1, title: 'aaa' }, // { id: 2, title: 'bbb' } ]
defghi1977, umyu👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2018/04/15 08:21

弱参照については理解していますか。
murabito

2018/04/15 08:24

いえ、理解していないと思います。分かっているのはコードベースで確認出来ているようなものくらいで、weakMapの場合、Mapと違って、x = {}と代入して元々xに入っていたオブジェクトへの参照を切ると、それが残らなくなるということくらいです。あれ、普通のオブジェクトの場合もこうなのではないですか?
murabito

2018/04/15 08:28

オブジェクトの場合も残るのですね。追記します。
defghi1977

2018/04/15 08:28

ガベージコレクションとメモリリークと弱参照で調べてみよう
murabito

2018/04/15 08:30

これから外出なので移動中の電車でスマホで調べてみます。
think49

2018/04/15 08:33 編集

あと、「スコープを分割出来る事」がありますが、この解説が載っている記事は見たことがないですね(stackoverflowに載ってるのかな)。いつかまとめようとは思っていたのでまとめますが、もう少し、自分で調べてから質問してほしかったです。
think49

2018/04/15 08:35

「WeakMap Map 違い」や「WeakMap 弱参照」や「WeakMap 性質」でググれば、いくらか情報が出てくるはずです。
guest

回答1

0

ベストアンサー

オブジェクト, Map, WeakMap の使い分け方

下記にまとめました。

Map と WeakMap は発想が逆のイメージ

私は WeakMap の発想の原点が Node#setUserData の代替APIとして認識していたところが大きいのですが、感覚的には Map とは逆に考える事が多い気がします。
WeakMap はオブジェクトをキーにして値をセットするわけですが、感覚的には「オブジェクトに WeakMap の key を代入している」がしっくりきます。

JavaScript

1var wm = new WeakMap([[object, value]]); // こういうコードを書いた場合 2 3wm[object] = value; // 実際の挙動イメージはこちらが正しいが 4object[wm] = value; // 弱参照のイメージ的にはこちらの方がしっくりくる

なぜなら、object の参照が切れると、WeakMap 側の参照も切れる感覚が下段コードの方がイメージしやすいからです。
WeakMap 単位でスコープを分割するオブジェクトの「参照不可能なプロパティ」のように扱うはそのイメージで書いています。

ただし、key 単位でスコープを分割するwm[object] = value のイメージで書いているので、コードの書き方に依存する部分はあります。

key をオブジェクトにする意味

普通のオブジェクトはキーといったら文字列ですし、それにずっと慣れていたので、キーをオブジェクトにする意味がよく分かりません。

それはおそらく、今までの murabito さんはコードが完成した時点で満足してしまい、「完成したコードの改善点」や「別の書き方がないか試行錯誤する事」を怠っていたからだと思います。
一つの書き方しか知らないのでしたら、別の書き方を覚える必要もありませんが、複数の選択肢がなければ「最良のコード」を掴む事も出来ません。
もし、今までに「現在は出来ないけど、こんなことが出来たらいいのに!」と歯がゆい思いをする経験があれば、新しい機能を知った時に「これがあれば、今までできなかった~が出来るぞ」な着想が容易になります。
ある要件をコード化する場合に、一つのコードで満足せず、様々なパターンのコードを考え、それぞれの性質を理解するようにしてみて下さい。
そうすることで、様々なAPIの利点/欠点を見て取れるようになります。

オブジェクトは [key, value] の集合体ですが、key はString型に限定されています(Symbol 型もありますが、説明上、ここでは除外します)。
例えば、key を使用して四則演算するコードがあり、+ で和算を試みた場合、単純に指定すると文字列連結として扱われてしまうので、予め、Number 型に変換しておく必要があります。

JavaScript

1var key = '1'; 2 3console.log(1 + key); // "11" 4console.log(1 + Number(key)); // 2

対して、Map は Number 型を key に指定可能なので、型変換処理が不要です。便利ですね。

では、Object 型を指定するメリットは何なのか。
DOMノードにデータを埋め込みたいと思ったことはありませんか。
要素ノードならば、data-* 属性がありますが、これは String 型に限定されています。
WeakMap を使用すれば、要素ノードに任意の値を結びつけることが可能です。

JavaScript

1var element = document.getElementById('sample'), 2 wm = new WeakMap([[element, 'Hello, World!']]); 3 4console.log(wm.get(element)); // "Hello, World!"

今まで、これと同等のコードを書くには、二次元配列を検索する必要がありました。

JavaScript

1var mapLike = [[element1, 'Hello, World!'], [element2, 'Hello, JavaScript!'], [element3, 'Hello, ECMAScript!']]; 2 3console.log(mapLike.find(array => target === array[0])[1]);

WeakMapはこのコードよりシンプルですし、弱参照なのですから、使わない理由はありません。

更新履歴

  • 2018/04/16 0:48 [JavaScript] オブジェクト, Map, WeakMap の使い分け方.md ->「スコープを分割する」節を「key 単位でスコープを分割する」にリネームし、「WeakMap 単位でスコープを分割する」と「オブジェクトの「参照不可能なプロパティ」のように扱う」節を追加
  • 2018/04/16 01:07「Map と WeakMap は発想が逆のイメージ」節を追記
  • 2018/04/16 23:07 「key をオブジェクトにする意味」節を追記

Re: murabito さん

投稿2018/04/15 14:08

編集2018/04/16 14:10
think49

総合スコア18162

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2018/04/15 14:44

いくつかtypoがあり、ちまちまと直しています。 説明にあたってコードブロック部分は全て検証済ですが、機能比較表は記憶で書いている部分もあります。 間違いは指摘頂ければ、直します。
think49

2018/04/15 16:14

new WeakMap と「キーとなるオブジェクト」の立ち位置を逆転させると、イメージしやすいかもしれません。 それが全てとはいいませんが、そう考える事が多々あります。 詳しくは親記事に追記しました。
defghi1977

2018/04/15 22:46

要するに, 「あるオブジェクトと生死を共にして欲しいデータは, 通常そのオブジェクトのプロパティに設定する必要があるが, WeakMapを使うと割と自由に保管することが出来るようになる」と ※この勢いでWeakSetについても言及されませんか?JavaScriptでは専らオブジェクト生成経緯の確認にしか使い途がないという.
think49

2018/04/16 11:57

To: defghi1977 さん > 要するに, 「あるオブジェクトと生死を共にして欲しいデータは, 通常そのオブジェクトのプロパティに設定する必要があるが, WeakMapを使うと割と自由に保管することが出来るようになる」と この説明、簡潔でいいですね。 > ※この勢いでWeakSetについても言及されませんか?JavaScriptでは専らオブジェクト生成経緯の確認にしか使い途がないという. 実は、WeakSet は使った事がなくて…(Set はあるのですが)。 配列, Set の対比は面白そうに感じています。qiita で速度を比較検証している記事がありましたが、Set が群を抜いて速かったですね(オブジェクト初期化子のキー検索と同等だった記憶があります)。
murabito

2018/04/16 13:25

think49さん、非常に詳細な解説ありがとうございます。ちょっと自分にはまだ内容が難しいようで、think49さんの記事を読んで途中で止まり、他の記事をいくつかまた読んで、また、think49さんの記事に戻るみたいなことを繰り返しているところです。質問本文に追記したのですが、そもそも自分はmapにしろweakmapにしろ、キーに文字列以外の例えばオブジェクトなりpromiseなり配列なりを使う必要性や意味が分かっておらず、そこがキーがオブジェクトであることが必須のWeakMapの理解を妨げている一因であることに気づきました。普通のオブジェクトはキーといったら文字列ですし、それにずっと慣れていたので、キーをオブジェクトにする意味がよく分かりません。普通のオブジェクトには確かに余計なメソッドやプロパティーが生えてはいるし列挙順の保証もないけど、オブジェクトで良いのでは無いかとすら感じてしまいます。
think49

2018/04/16 14:37

To: murabito さん 親記事に追記しました。 prototypeを使わず、平易な説明にしましたが、本格的に理解するには、前提となる基礎知識を身に着ける必要性を感じます。 1. オブジェクト初期化子を二次元配列([[key1,value1],[key2,value2]])化し、Object.keys, obj[key], Object.values(obj) を再現する関数を作る 2. 配列で可能な事をオブジェクト初期化子で一通り再現してみる 3. オブジェクトと二次元配列のメリット/デメリット(1, 2 のまとめ) 4. オブジェクト初期化子は new Object である(Object.prototypeのプロパティを利用できる) 5. プロトタイプチェーンを理解する 6. Object.create(null) 7. Map 8. WeakMap 課題としてはこんなところ(WeakMap は最後です)。
murabito

2018/04/18 14:35

think49さん、ありがとうございます。課題として挙げていただいたことに取り組んでみたいと思います。 また、前提となる知識が身についた段階でWeakMapに関する質問を別の切り口で質問させて頂くかもしれませんが、一旦、この質問は閉じたいと思います。 ちなみになのですが、こちらの1つ目の意味がよく理解できていません。 オブジェクト初期化子を二次元配列([[key1,value1],[key2,value2]])化し、Object.keys, obj[key], Object.values(obj) を再現する関数を作る これは、`{key1: 'aaa', key2: 'bbb'`のようなオブジェクトを引数に渡すと、そのオブジェクトのキーのリストを返す関数を作る、`{key1: 'aaa', key2: 'bbb'`のようなオブジェクトと任意の1つのkey名を引数に渡すと、そのkeyに対する値を返す関数を作る、`{key1: 'aaa', key2: 'bbb'`のようなオブジェクトを引数に渡すと、そのオブジェクトの値のリストを返す関数を作る、ということでしょうか?
think49

2018/04/18 14:51

var obj = {a:1,b:2,c:3}, array = [['a', 1],['b',2],['c',3]]; obj.a と同等の動作を array.get('a') なり get(array, 'a') で実行する関数を作る obj.hasOwnProperty('a') と同等の動作を array で実行する関数を作る Object.keys(obj)と同等の動作を... Object.values(obj)と同等の動作を... Object.entries(obj)と同等の動作を... のようにオブジェクト初期化子で実現可能な機能全てを二次元配列でエミュレーションしてみることです。 逆に二次元配列で出来る事をオブジェクト初期化子に移植してもいいですし、Map で出来る事を移植してみるのもいいと思います。 そうすることで、「ある機能を実現するにはMapの方が便利」や「これとこれとこれを使いたいけど、対応するAPIがない。MapにArray.prototype.reduce を移植して使おう」のようにあらゆる選択肢から最適解を導き出す事が出来ます。 実際、MapにArray.prototype系メソッドが欲しくなる場面は多いので、配列の機能をMapやObjectに移植する試みは有用だと思います。
murabito

2018/04/21 03:31

ありがとうございます。少し時間はかかりそうですが、手の空いてる時間を見つけて少しずつ課題の方に取り組んでみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問