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

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

ただいまの
回答率

90.86%

  • JavaScript

    14769questions

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

  • Node.js

    1673questions

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

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

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 4
  • VIEW 524

murabito

score 15

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

  1. WeakMapの使い道が分からなかったり、これを使うと何が良いのかが分かりません。
    そこをクリアにしたいです。

  2. 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' } ]
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • murabito

    2018/04/15 17:30

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

    キャンセル

  • think49

    2018/04/15 17:33 編集

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

    キャンセル

  • think49

    2018/04/15 17:35

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

    キャンセル

回答 1

checkベストアンサー

+4

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

下記にまとめました。

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

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

var wm = new WeakMap([[object, value]]);  // こういうコードを書いた場合

wm[object] = value;  // 実際の挙動イメージはこちらが正しいが
object[wm] = value;  // 弱参照のイメージ的にはこちらの方がしっくりくる

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

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

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

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

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

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

var key = '1';

console.log(1 + key);         // "11"
console.log(1 + Number(key)); // 2

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

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

var element = document.getElementById('sample'),
    wm = new WeakMap([[element, 'Hello, World!']]);

console.log(wm.get(element)); // "Hello, World!"

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

var mapLike = [[element1, 'Hello, World!'], [element2, 'Hello, JavaScript!'], [element3, 'Hello, ECMAScript!']];

console.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 23:44

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

    キャンセル

  • 2018/04/16 01:14

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

    キャンセル

  • 2018/04/16 07:46

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

    キャンセル

  • 2018/04/16 20:57

    To: defghi1977 さん
    > 要するに, 「あるオブジェクトと生死を共にして欲しいデータは, 通常そのオブジェクトのプロパティに設定する必要があるが, WeakMapを使うと割と自由に保管することが出来るようになる」と
    この説明、簡潔でいいですね。

    > ※この勢いでWeakSetについても言及されませんか?JavaScriptでは専らオブジェクト生成経緯の確認にしか使い途がないという.
    実は、WeakSet は使った事がなくて…(Set はあるのですが)。
    配列, Set の対比は面白そうに感じています。qiita で速度を比較検証している記事がありましたが、Set が群を抜いて速かったですね(オブジェクト初期化子のキー検索と同等だった記憶があります)。

    キャンセル

  • 2018/04/16 22:25

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

    キャンセル

  • 2018/04/16 23: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 は最後です)。

    キャンセル

  • 2018/04/18 23: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'`のようなオブジェクトを引数に渡すと、そのオブジェクトの値のリストを返す関数を作る、ということでしょうか?

    キャンセル

  • 2018/04/18 23: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に移植する試みは有用だと思います。

    キャンセル

  • 2018/04/21 12:31

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

    キャンセル

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

  • ただいまの回答率 90.86%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • JavaScript

    14769questions

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

  • Node.js

    1673questions

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