3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

jQueryAdvent Calendar 2017

Day 15

$().data 完全解析

Last updated at Posted at 2017-12-14

jQueryの使われたコードからjQueryを外そうとしてふと手が止まるものの1つ、$().dataについて掘り下げてみます。

なお、コードベースとしては、公式サイトにおいてある、開発版の3.2.1を使います。

2つの機能

$().dataは、元来は「DOM要素にJavaScriptの値を紐付ける」だけの機能だったのですが、あとからHTMLの方にdata-*属性が出現した結果、以下の2ステップを経るようなメソッドとなってしまいました。

  1. 読み出し時点で値がなければ、data-*属性を参照する
  2. それ以降は、従来通りjQuery内部のデータだけ参照する

ということで、この2つが中途半端につながって、混乱を招く結果となっています。

data-*属性の読み出し

まず、data-*属性が読み出されるのは、以下のような場合に限られます。

  • $(elem).data()と全データをリクエストした場合(ただし、1要素あたり1回限りかつ、その時点でデータがセットされていない場合のみ)
  • keyに対応するデータをセットせずに$(elem).data('key')で値を参照した場合(かつ、jQuery側にundefined以外のデータが入っていない場合)

以上の条件に当てはまる場合、data-*属性から値を読み出しますが(camelCaseというキーはdata-camel-caseのように変換されます)、取り出した文字列には以下のような変換が入ります。

  • truefalsenullの場合…対応するJavaScriptの値になる
  • 数値…JavaScriptの数値と同じ表記の場合に限って、数値に変換される(たとえば、3は数値に変換されるけど、3.0+3はされない、など)
  • {...}[...]の形…JSONとみなして変換を試みる(失敗してもtry-catchして文字列そのままになる)

データの保存/読み出し

では、次はDOMノードへのデータ保存/読み出しをどのようにしているか見ていきましょう。jQueryのソースにはこんなコメントが付いています。

jquery.js
//	Implementation Summary
//
//	1. Enforce API surface and semantic compatibility with 1.9.x branch
//	2. Improve the module's maintainability by reducing the storage
//		paths to a single mechanism.
//	3. Use the same single mechanism to support "private" and "user" data.
//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
//	5. Avoid exposing implementation details on user objects (eg. expando properties)
//	6. Provide a clear path for implementation upgrade to WeakMap in 2014

ということで、「WeakMapの導入も視野に入れた」「シンプルな仕組み」が構築されています。

具体的に仕組みを追いかけてみると、Dataというクラスが存在して、インスタンスそれぞれにユニークなIDであるexpandoプロパティを持っています。そして、データをセットできるのは「ノード以外のJavaScriptオブジェクト」「エレメントノード」「ドキュメントノード」に限定されていて、コメントノードやテキストノードは処理から除外されます。

昔はDOMオブジェクトにそのまま値を割り当ててしまうと、DOMとJavaScriptをまたいだ循環参照が発生して、メモリリークしてしまうという問題があったため、expando名のプロパティにはキーだけ入れて、実際の中身は別に保管する、というような仕組みが必要だったのですが、jQuery 3.2.1はIE 9以上が対象ということで、そのような問題が起きないからか、DOMにexpandoの名前で追加したプロパティに直接値を書き込む、というシンプルな仕組みとなっていました。

…というか、モダンブラウザが前提なら、勝手にDOMへプロパティを増やしても(名前の衝突を別にすれば)問題にならない、ということなんですね。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?