t_hazawaの日記

株式投資とWebエンジニアリングのブログです。株式投資の目次は→です。 https://t-hazawa.hatenablog.com/entry/2021/02/12/220933

JavaScript中級者になろう 14章を読んだ (データのローカル保存)

前: JavaScript中級者になろう 13章を読んだ (通信) - t_hazawaの日記

経緯

  • JS力をつけていっている

十四章第一回 Storage

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

  • cookieとの違いは、「データがサーバーへ送られるか送られないか」です storageはサーバー側からは参照できません。javascriptからのみです 基本的にはこちらのほうが便利

  • 一つはlocalStorageであり、もうひとつはsessionStorageです

  • window.localStorage, window.sessionStorage
  • localStorageは同じオリジンなら、違うページからでも同じ内容を共有できる

  • sessionStorageはタブごとにことなる 2021/12/09 0:05
  • 基本は、storageのプロパティに値を入れるとそれがそのまま保存されます。実は、保存できる値は文字列に限ります。オブジェクトとかを保存したい場合はjsonにしましょう

  • localStorage.foo="bar"; localStorage.setItem("foo","bar");

  • getItem もあるが普通にプロパティ参照でいける
  • removeItem
  • clear そのStorageのデータが全て消える
  • length
  • var key=localStorage.key(0); //0番目のキーを取得

  • window.addEventListener("storage",function(e){ },false);

  • Storageに変更があったら window に storage イベントが発生するらしい
  • ev.key ev.oldValue ev.newValue ev.url ev.storageArea
  • 大体は自分で変更したものだろうどイベントがくるんだなあ(?) ←これは違うらしい
  • 共有しているStorage (localStorage)で変更があると、他のタブでイベントがくるらしい
  • clearメソッドの場合は、key,oldvalue,newvalueが全てnullになっています。

  • コンソールで localStorage.clear() してもイベントは飛ばないようだ
  • Storageは expireしないのね (便利)
  • sessionStorage は、一度タブを閉じると1からになってしまいますね
  • sessionStorageには、一時的なデータを入れるといいね (session認証情報ではないね)

十四章第二回 Indexed Database

https://uhyohyo.net/javascript/14_2.html

  • IndexedDBだけで4回ある (使わなさそうだけど、使わなさそうなのはこれだけなのでまぁ全部読んでいいか)
  • 通称IndexedDBは、前回のStorageが進化したようなもの ある種のデータベースをjavascriptから作ってブラウザに保存しておいてもらえる

  • オブジェクト等を保存できる点や検索機能がある

  • ひとつひとつのデータはレコード(record)といい、レコードはkeyとvalueを持つ

  • データベースはオブジェクトストアを保持して、レコードはオブジェクトストアの中に保存される レコードはオブジェクトストアの中に保存されていて、1つ以上のオブジェクトストアをまとめたものがデータベース

  • データベースのなかにオブジェクトストア(MySQLのテーブルみたいなもの?)があるらしい
  • IDBRequestというオブジェクト IndexedDBに対して何か操作を行った時の結果を表すオブジェクト

  • このオブジェクトでsuccess イベントや error イベントが発生する 結果はresultプロパティに入ってる
  • この下位は 8800文字ある 前回は 4900字
  • IDBRequestのreadyStateプロパティ でも終わったかわかるぞ "pending" "done"
  • error, source, transaction プロパティもあるぞ
  • window.indexedDB でアクセス
  • windowのプロパティはグローバル変数でアクセスかのうなので indexedDB でもいける
  • open() がまずいる
  • openはデータベースを開くメソッドですが、そのデータベースが存在しない場合は作ってくれます。つまり、とにかくデータベースを使いたければopenということです。

  • 気軽なJS
  • 引数で指定したバージョンが開こうとしたデータベースより低い場合(すなわち、保存されているデータベースのバージョンが高いのにそれより低いバージョンのデータベースを要求した場合)、このアプリは新しいデータベースに対応していなくて誤動作を起こす可能性があるということなので、エラーを起こしデータベースは開けません。

  • openのバージョン番号を上げればバージョンが上がる
  • バージョン番号は省略可能(うひょさん非推奨)
  • 無事データベースを開けた場合は、IDBOpenDBRequestのresultプロパティには、IDBDatabaseのインスタンスであるオブジェクトが入っています これを用いていよいよデータベースの中身をいじることができます

  • var request = indexedDB.open("test",1); //testというデータベースをバージョン1で開く。openの返り値はIDBOpenDBRequest

  • データベースが処理中で待つ時は blocked イベントが発生する
  • データベースの名前は一度作ったら変えられない

  • close すると多分親切 しなくてもいいのかも?
  • 基本的に一つのアプリケーションなら一つのデータベースで完結するのがよいのではないでしょうか。

  • createObjectStoreの返り値は、IDBObjectStoreというオブジェクト

  • あと39回でおわり (全部で121回 2/3終わってるね)
  • versionchangeトランザクション 実はcreateObjectStoreはversionchangeトランザクション中しか使えないメソッドなので、必然的にこれを使うことになります。

  • バージョン変わるときにしかオブジェクトストアは作成できないのだ
  • versionchangeトランザクションが発動した場合、openの返り値であるIDBOpenDBRequestにおいて、upgradeneededイベントが発生します

  • このイベントの中で createObjectStore とかするらしい
  • db.createObjectStore("foo", { keyPath:"hoge", autoIncrement:true });

  • keyについては次回

十四章第三回 Indexed Database 2

https://uhyohyo.net/javascript/14_3.html

  • オブジェクトストアごとにキージェネレーターを設定できる auto increment
  • 配列、文字列、dateオブジェクト、数値(0以下の数や小数も含む) をキーに使える
  • keyというのは順番に並べることができなければいけません。これは並び替えやインデクシングなどの機能を提供するためです。

  • 異なる種類の値については、大きい方から、配列、文字列、date、数値の順になっています 文字列は文字コード順、Dateは日付順、数値は大きさ順

  • 2つの配列の、0番目、1番目、・・・どうしの要素を比較して、どちらかが大きければ、そちらの配列が大きくなります 長いほうが大きくなります。長さまで同じ場合は配列が完全に同じなので、同じ

  • key の種類が混在しててもOKらしい (フランクなJS)
  • keypathにはkeyのプロパティ名を文字列で指定します 省略すると out-of-line key になる(目に見えないどこか)

  • 自動で key になったりしないのね
  • userid も key にできて便利なのだ
  • "foo.bar"がkeyPath だと、 foo オブジェクトの中の bar がキー 。 柔軟
  • キージェネレータを使うことのメリットは、一意な連番を勝手にレコードに割り振ってくれることです

  • 結構使われるらしい
  • データベースを操作するにはまずトランザクション(transaction)を作ります read でも必要 IDBDatabaseのtransactionメソッド 返り値としてIDBTransactionオブジェクト これがトランザクションを操作するオブジェクト

  • トランザクションをつくるときにオブジェクトストアを指定しないといけないので、joinとかはなさそう
  • トランザクションを "readonly" にしてデータを守ろう というか、複数同時にデータを読めるぞ
  • "readwrite"だと順番待ちになる
  • 第2引数が省略された場合はreadonly

  • 配列を用いて複数のオブジェクトストア名を同時に指定することが可能です。この場合ひとつのトランザクションで複数のオブジェクトストアを同時に扱えます

  • joinありそう
  • abort()で強制的にtransaction失敗
  • IDBObjectStore というオブジェクトを取得してデータを読み書きしよう IDBTransactionのobjectStoreメソッド
  • versionchangeトランザクションはreadwriteトランザクションの上位互換で、データベースへの読み書きが行えるほか、オブジェクトストアを作ったり消したりすることまでできます(

  • データベースに入れることができるものの幅はけっこう広いです。 さらにdateオブジェクトや正規表現オブジェクト、fileやblob、filelist(十二章第五回)やimagedata(これはまだ解説していませんが、canvasを扱うときに出てきます)もokです

  • 以上のものを要素としてもつ配列やオブジェクトもokです

  • 入れることができないのは、関数や、nodeのようなdom関係のオブジェクトです。。。また、オブジェクトについたゲッタやセッタや、enumerableなどのプロパティの属性はコピーされず、デフォルトの状態になる

  • in-line keyの場合には、keyはオブジェクトのプロパティとなります。つまりプリミティブなどはプロパティを持てないのでレコードとしては不適で、エラーになります。

  • 配列はin-line key だとだめよ オブジェクトじゃないから
  • 逆にout-of-line keyの場合には、レコードそのものはkey情報を持たないので、レコードにこれ以上の制限はありません。

  • オブジェクトには代入してもかえられないプロパティがあるよ
  • add, putでれこーど追加
  • 第1引数が追加するレコード、第二引数がそのレコードにつけるkeyです。。。ただし、第2引数のkeyというのは、out-of-line keyのときしか使えません ii

  • キージェネレーターがアッテョ inline-key のときはkeyをレコードにするオブジェクトからしョう略デキる
  • addは重複エラー、putは上書き
  • keyとして正の数値を指定した場合で、かつその値がキージェネレータから発生する次の数値以上の場合、キージェネレータの数値が「その値より大きい最小の整数」に変更されます。

  • 便利
  • オブジェクトストアに対するひとつひとつの操作をリクエストと呼び、その結果を知らせてくれるのがidbrequestです。

  • requestへの結果だけど request
  • IDBRequest.result だからよさそう .error とかあるし
  • idbrequestではイベントが発生する可能性があります。成功したときはsuccessイベント、失敗したときはerrorイベント

  • successイベントは次の処理に進むためによく使いますし、errorイベントは、前述のように、中途半端に失敗したらトランザクションを中断して無効にするというときに、失敗の検出をするために使えます

  • addやputの場合には、idbrequestのresultは追加されたレコードのkey

  • あれもこれも request と名前つけるのはわかりにくそう。。
  • ちなみに、データベースの中身は、Chromeの場合だとDeveloper toolsのApplicationsから見ることが可能です。試しに見てみましょう。

  • あまり使われてるシーンに遭遇しないから実感がない感じ。。

十四章第四回 Indexed Database 3

https://uhyohyo.net/javascript/14_4.html

  • .get() なかったら undefined
  • .delete() resultは特にありません
  • .clear() オブジェクトストアのレコードを全部消す
  • Key Range でまとめて取得や削除ができるぞ
  • key rangeは2つの端点によって定義されます。範囲の中で最小の点と最大の点ですね。 key rangeは、IDBKeyRangeというオブジェクトで表されるのですが、作り方がほんの少し特殊

  • var range=IDBKeyRange.bound(3, 5, true, true); 開いてればtrue 閉じてれば false つまり 3< x < 5

  • 範囲の上限を指定しないkey rangeを作るには、lowerBoundメソッド upperBound only

  • さて、そうして得られたidbkeyrangeには、lower,upper,loweropen,upperopenという4つのプロパティがあります

  • このようにして作ったidbkeyrangeは、keyのかわりにgetやdeleteの引数として使えます。ただし、getにidbkeyrangeを渡しても、レコードが複数得られるわけではありません。当てはまる最初の一つのレコードのみ帰ってきます。一方deleteの場合は、当てはまるレコードを全部消去してくれます。

  • count にこのkey range を渡すとレコード数がかえってくる
  • .getAll とか .gets() みたいなのはなさそう
  • そこで登場するのがカーソル(cursor)です

  • "next" とか "prev" とか動く順番を指定できる
  • "nextunique"。nextと同じですが、重複するkeyをもつレコードは最初のものだけ見ます。。"prevunique"

  • key は重複できたんだ?
  • これは次回の話に関わってきますので、今は深く考える必要はありません。惑わされず、keyは重複しないということは覚えておいてください。

  • 奥深い Indexed DB でも、だいたいサーバにデータを保存するからあまり使われてなさそう
    • まぁ、使わなさ層のは、この講座でこのindexed db が最後だからまぁいいかな(?)
  • openCursorの返り値も、例のごとくIDBRequestです。resultプロパティの値として、カーソルを示すIDBCursorWithValueオブジェクト カーソルが現在いる位置のレコードの内容を示す、keyとvalueというプロパティを持ちます

  • advance(susumu_kazu) 戻れない

  • IDBRequst の success に通知がくるのこと
  • 最後はresult が null になるとのこよ
  • countinue(key_name) そこまですすむ
  • getAll がほしそう
  • updateです。これは、現在の位置のレコードを書き換えるというメソッド key はかえられない deleteもある

  • とても手順が大変な indexeddb

十四章第五回 Indexed Database 4

https://uhyohyo.net/javascript/14_5.html

12600文字

  • id, name, age がある場合
  • そのときには、このオブジェクトストアに対して、新たにageをkeypathとするインデックスを作ってそれを使えばいいのです 実はインデックスは自身のkeypathを持ちます インデックスは基本的に、レコードがオブジェクトでないと効果を発揮しません

  • 複数のインデックスを晴れるのね
  • versionchange トランザクションでしか新インデックスはれないよ
  • IDBObjectStoreのメソッドであるcreateIndex uniqueとmultiEntry

  • multiEntryがtrueになると、検索時の配列の挙動が変わります。配列の要素のうち、どれか一つでも条件を満たせば当てはまるようになるのです

  • deleteIndex
  • IDBIndexのgetとかでレコードを取得してね
  • 実はインデックスのkeyPathには、文字列の他に、複数のkeyPathを並べた配列も許可されています

  • もしインデックスのkeyPathが次のような配列だったとします。

  • ["party","age"]
  • このとき、このインデックスにおけるさっきのレコードのkeyは次の配列になります。
  • ["自民党",72]
  • さらに、範囲指定の例も紹介しておきます。今回の場合、政党名を1つに絞ったうえで年齢の範囲を指定することができます。

  • 範囲指定では使いにくいかもしれませんが、並び替えのためなら配列のkeypathも使い道があるかもしれません。

  • 終了してすることが何も無くなった状態を、イベントループに戻ったなどといいます。この状態になって、なおかつもう処理すべきリクエストが残っていないときが、トランザクションが終了したときです。

  • 複数のカーソルを同時に動かしたりした場合など、どちらが先に終わるのかわからないので面倒です。その場合はcompleteイベントが役に立ちます。。

  • 検索条件として範囲指定しかできませんし、複数の条件を同時に指定するのは難しいです

  • 実際には、スケールにもよりますが、とりあえずkey rangeで一次的に絞り込んで、その後は検索結果に対してif文などで絞り込んでいくことも

  • 操作がめんどそうだった

感想

  • localStorage は使いそうだった
  • IndexedIndexはとても煩雑そうで使いにくそうだった

時間まとめ

  • 2021/12/9-14
  • 10 + 44 + 30 + 30 + 32 + 14(ブログ) = 160分
  • 累計: 1601分 (= 26.7時間)