Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LocalStorage, sessionStorage #217

Merged
merged 2 commits into from
Oct 5, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 99 additions & 98 deletions 6-data-storage/02-localstorage/article.md
Original file line number Diff line number Diff line change
@@ -1,83 +1,86 @@
# LocalStorage, sessionStorage

Web storage objects `localStorage` and `sessionStorage` allow to save key/value pairs in the browser.
Objek penyimpanan web `localStorage` dan `sessionStorage` memungkinkan untuk menyimpan pasangan *key*/*value* di peramban.

What's interesting about them is that the data survives a page refresh (for `sessionStorage`) and even a full browser restart (for `localStorage`). We'll see that very soon.
Yang menarik dari mereka adalah bahwa data bertahan dari refresh halaman (untuk `sessionStorage`) dan bahkan ketika peramban dimulai ulang secara penuh (untuk `localStorage`). Kita akan melihatnya segera.

We already have cookies. Why additional objects?
Kita sudah memiliki cookies. Mengapa perlu tambahan objek?

- Unlike cookies, web storage objects are not sent to server with each request. Because of that, we can store much more. Most browsers allow at least 2 megabytes of data (or more) and have settings to configure that.
- Also unlike cookies, the server can't manipulate storage objects via HTTP headers. Everything's done in JavaScript.
- The storage is bound to the origin (domain/protocol/port triplet). That is, different protocols or subdomains infer different storage objects, they can't access data from each other.
- Tidak seperti cookies, objek penyimpanan web tidak dikirim ke server pada setiap permintaan. Karena itu, kita bisa menyimpan lebih banyak. Sebagian besar peramban mengizinkan setidaknya 2 *megabyte* data (atau lebih) dan memiliki pengaturan untuk konfigurasinya.
- Juga tidak seperti cookies, server tidak dapat memanipulasi objek penyimpanan melalui HTTP header. Semuanya dilakukan dalam JavaScript.
- Penyimpanan terikat kepada asalnya (domain/protokol/port triplet). Artinya, protokol atau subdomain yang berbeda menyimpulkan objek penyimpanan yang berbeda, mereka tidak dapat mengakses data satu sama lain.

Both storage objects provide same methods and properties:
Kedua objek penyimpanan menyediakan *method* dan properti yang sama:

- `setItem(key, value)` -- store key/value pair.
- `getItem(key)` -- get the value by key.
- `removeItem(key)` -- remove the key with its value.
- `clear()` -- delete everything.
- `key(index)` -- get the key on a given position.
- `length` -- the number of stored items.
- `setItem(key, value)` -- menyimpan pasangan *key*/*value*.
- `getItem(key)` -- mendapatkan *value* dengan sebuah *key*.
- `removeItem(key)` -- menghapus *key* beserta *value*-nya.
- `clear()` -- menghapus semuanya.
- `key(index)` -- mendapatkan *key* pada posisi tertentu.
- `length` -- jumlah item yang disimpan.

As you can see, it's like a `Map` collection (`setItem/getItem/removeItem`), but also allows access by index with `key(index)`.
Seperti yang Anda lihat, ini seperti koleksi `Map` (`setItem/getItem/removeItem`), tetapi juga memungkinkan akses berdasarkan indeks dengan `key(index)`.

Let's see how it works.
Mari kita lihat cara kerjanya.

## localStorage demo
## Demo localStorage

The main features of `localStorage` are:
Fitur utama `localStorage` adalah:

- Shared between all tabs and windows from the same origin.
- The data does not expire. It remains after the browser restart and even OS reboot.
- Dibagikan antara semua *tab* dan *window* dari *origin* yang sama.
- Data tidak kedaluwarsa. Data tetap tersimpan setelah peramban *restart* dan bahkan setelah OS *reboot*.

For instance, if you run this code...
Misalnya, jika Anda menjalankan kode ini...

```js run
localStorage.setItem('test', 1);
```

...And close/open the browser or just open the same page in a different window, then you can get it like this:
...Dan kemudian menutup/membuka peramban atau hanya membuka halaman yang sama di jendela yang berbeda, maka Anda bisa mendapatkannya seperti ini:

```js run
alert( localStorage.getItem('test') ); // 1
```

We only have to be on the same origin (domain/port/protocol), the url path can be different.

The `localStorage` is shared between all windows with the same origin, so if we set the data in one window, the change becomes visible in another one.
Kita hanya harus berada di *origin* yang sama (domain/port/protocol), *path* urlnya bisa berbeda.

## Object-like access
`localStorage` dibagi antara semua *window* dengan *origin* yang sama, jadi jika kita mengatur data di satu *window*, perubahan akan terlihat di *window* lain.

## Akses seperti objek

We can also use a plain object way of getting/setting keys, like this:

Kita juga dapat menggunakan cara sebuah objek biasa untuk mendapatkan/mengatur *key*, seperti ini:

```js run
// set key
// mengatur key
localStorage.test = 2;

// get key
// mendapatkan key
alert( localStorage.test ); // 2

// remove key
// menghapus key
delete localStorage.test;
```

That's allowed for historical reasons, and mostly works, but generally not recommended, because:
Itu diizinkan karena alasan historis, dan sebagian besar berfungsi, tetapi umumnya tidak disarankan, karena:

1. If the key is user-generated, it can be anything, like `length` or `toString`, or another built-in method of `localStorage`. In that case `getItem/setItem` work fine, while object-like access fails:
1. Jika *key* dibuat oleh pengguna, itu bisa apa saja, seperti `length` atau `toString`, atau *method* bawaan `localStorage` lainnya. Dalam hal itu `getItem/setItem` berfungsi dengan baik, sementara akses seperti objek akan gagal:
```js run
let key = 'length';
localStorage[key] = 5; // Error, can't assign length
```
2. Ada *event* `storage`, yang dipicu saat kita mengubah data. *Event* itu tidak terjadi untuk akses seperti objek. Kita akan melihatnya nanti di bab ini.

2. There's a `storage` event, it triggers when we modify the data. That event does not happen for object-like access. We'll see that later in this chapter.

## Looping over keys
## Pengulangan pada keys

As we've seen, the methods provide "get/set/remove by key" functionality. But how to get all saved values or keys?
Seperti yang telah kita lihat, *method* menyediakan fungsionalitas "mendapatkan/mengatur/menghapus dengan *key*". Tetapi bagaimana cara mendapatkan semua *value* atau *key* yang disimpan?

Unfortunately, storage objects are not iterable.
Sayangnya, objek penyimpanan tidak bisa dilakukan iterasi.

One way is to loop over them as over an array:
Salah satu caranya adalah dengan mengulangnya sebagai senarai (array):

```js run
for(let i=0; i<localStorage.length; i++) {
Expand All @@ -86,29 +89,28 @@ for(let i=0; i<localStorage.length; i++) {
}
```

Another way is to use `for key in localStorage` loop, just as we do with regular objects.
Cara lain adalah dengan menggunakan perulangan `for key in localStorage`, seperti yang kita lakukan dengan objek reguler.

It iterates over keys, but also outputs few built-in fields that we don't need:
Ini melakukan iterasi pada *key*, tetapi juga menampilkan beberapa *field* bawaan yang tidak kita perlukan:

```js run
// bad try
// Percobaan yang buruk
for(let key in localStorage) {
alert(key); // shows getItem, setItem and other built-in stuff
alert(key); // menampilkan getItem, setItem and hal bawaan lainnya
}
```

...So we need either to filter fields from the prototype with `hasOwnProperty` check:

...Jadi kita perlu memfilter *field* dari *prototype* dengan pemeriksaan `hasOwnProperty`:
```js run
for(let key in localStorage) {
if (!localStorage.hasOwnProperty(key)) {
continue; // skip keys like "setItem", "getItem" etc
continue; // melewati keys seperti "setItem", "getItem" etc
}
alert(`${key}: ${localStorage.getItem(key)}`);
}
```

...Or just get the "own" keys with `Object.keys` and then loop over them if needed:
...Atau jika hanya mendapatkan *key*-nya saja dengan `Object.keys` dan kemudian dilakukan perulangan jika diperlukan:

```js run
let keys = Object.keys(localStorage);
Expand All @@ -117,21 +119,20 @@ for(let key of keys) {
}
```

The latter works, because `Object.keys` only returns the keys that belong to the object, ignoring the prototype.

Yang terakhir berfungsi, karena `Object.keys` hanya mengembalikan kunci milik objek, mengabaikan *prototype*.

## Strings only
## Hanya string

Please note that both key and value must be strings.
Harap dicatat bahwa *key* dan *value* harus berupa string.

If were any other type, like a number, or an object, it gets converted to string automatically:
Jika ada tipe lain, seperti angka, atau objek, itu akan dikonversi menjadi string secara otomatis:

```js run
localStorage.user = {name: "John"};
alert(localStorage.user); // [object Object]
```

We can use `JSON` to store objects though:
Kita dapat menggunakan `JSON` untuk menyimpan objek:

```js run
localStorage.user = JSON.stringify({name: "John"});
Expand All @@ -141,7 +142,7 @@ let user = JSON.parse( localStorage.user );
alert( user.name ); // John
```

Also it is possible to stringify the whole storage object, e.g. for debugging purposes:
Juga dimungkinkan untuk *stringify* seluruh objek penyimpanan, sebagai contoh untuk tujuan *debugging*:

```js run
// added formatting options to JSON.stringify to make the object look nicer
Expand All @@ -151,99 +152,99 @@ alert( JSON.stringify(localStorage, null, 2) );

## sessionStorage

The `sessionStorage` object is used much less often than `localStorage`.
Objek `sessionStorage` lebih jarang digunakan daripada `localStorage`.

Properties and methods are the same, but it's much more limited:
Properti dan *method*-nya sama, tetapi jauh lebih terbatas:

- The `sessionStorage` exists only within the current browser tab.
- Another tab with the same page will have a different storage.
- But it is shared between iframes in the same tab (assuming they come from the same origin).
- The data survives page refresh, but not closing/opening the tab.
- `sessionStorage` hanya ada di dalam tab peramban saat ini.
- Tab lain dengan halaman yang sama akan memiliki penyimpanan yang berbeda.
- Tapi dibagi antar iframe di tab yang sama (dengan asumsi mereka berasal dari *origin* yang sama).
- Data bertahan dari *refresh* halaman, tetapi tidak dengan menutup/membuka tab.

Let's see that in action.
Mari kita lihat langsung prakteknya.

Run this code...
Jalankan kode ini...

```js run
sessionStorage.setItem('test', 1);
```

...Then refresh the page. Now you can still get the data:
...Kemudian *refresh* halaman. Sekarang Anda masih bisa mendapatkan data:

```js run
alert( sessionStorage.getItem('test') ); // after refresh: 1
alert( sessionStorage.getItem('test') ); // setelah refresh: 1
```

...But if you open the same page in another tab, and try again there, the code above returns `null`, meaning "nothing found".
...Tetapi jika Anda membuka halaman yang sama di tab lain, dan mencoba lagi di sana, kode di atas mengembalikan `null`, yang berarti "tidak ada yang ditemukan".

That's exactly because `sessionStorage` is bound not only to the origin, but also to the browser tab. For that reason, `sessionStorage` is used sparingly.
Itu persis terjadi karena `sessionStorage` terikat tidak hanya pada *origin*, tetapi juga pada tab peramban. Oleh karena itu, `sessionStorage` jarang digunakan

## Storage event
## Event storage

When the data gets updated in `localStorage` or `sessionStorage`, [storage](https://www.w3.org/TR/webstorage/#the-storage-event) event triggers, with properties:
Saat data diperbarui di `localStorage` atau `sessionStorage`, *event* [storage](https://www.w3.org/TR/webstorage/#the-storage-event) terpicu, dengan properti:

- `key` – the key that was changed (`null` if `.clear()` is called).
- `oldValue` – the old value (`null` if the key is newly added).
- `newValue` – the new value (`null` if the key is removed).
- `url` – the url of the document where the update happened.
- `storageArea` – either `localStorage` or `sessionStorage` object where the update happened.
- `key` – *key* yang diubah (`null` jika `.clear()` dipanggil).
- `oldValue` – *value* lama (`null` jika *key* baru ditambahkan).
- `newValue` – *value* baru (`null` jika key dihapus).
- `url` – url dokumen tempat pembaruan terjadi.
- `storageArea` – objek `localStorage` atau `sessionStorage` tempat pembaruan terjadi.

The important thing is: the event triggers on all `window` objects where the storage is accessible, except the one that caused it.
Yang penting adalah: *event* terpicu pada semua objek `window` tempat penyimpanan dapat diakses, kecuali yang menyebabkannya.

Let's elaborate.
Mari kita perjelas.

Imagine, you have two windows with the same site in each. So `localStorage` is shared between them.
Bayangkan, Anda memiliki dua *window* dengan situs yang sama di masing-masing *window*. Jadi `localStorage` dibagi di antara keduanya.

```online
You might want to open this page in two browser windows to test the code below.
Anda mungkin ingin membuka halaman ini di dua window peramban untuk menguji kode di bawah ini.
```

If both windows are listening for `window.onstorage`, then each one will react on updates that happened in the other one.
Jika kedua *window* mendengarkan (listening) `window.onstorage`, maka masing-masing akan bereaksi terhadap pembaruan yang terjadi di *window* lainnya.

```js run
// triggers on updates made to the same storage from other documents
window.onstorage = event => { // same as window.addEventListener('storage', event => {
// memicu pembaruan yang dibuat ke penyimpanan yang sama dari dokumen lain
window.onstorage = event => { //sama seperti window.addEventListener('storage', event => {
if (event.key != 'now') return;
alert(event.key + ':' + event.newValue + " at " + event.url);
};

localStorage.setItem('now', Date.now());
```

Please note that the event also contains: `event.url` -- the url of the document where the data was updated.
Harap perhatikan bahwa *event* juga berisi: `event.url` -- url dokumen tempat data diperbarui.

Also, `event.storageArea` contains the storage object -- the event is the same for both `sessionStorage` and `localStorage`, so `event.storageArea` references the one that was modified. We may even want to set something back in it, to "respond" to a change.

**That allows different windows from the same origin to exchange messages.**
**Itu memungkinkan window yang berbeda dari origin yang sama untuk bertukar pesan.**

Modern browsers also support [Broadcast channel API](mdn:/api/Broadcast_Channel_API), the special API for same-origin inter-window communication, it's more full featured, but less supported. There are libraries that polyfill that API, based on `localStorage`, that make it available everywhere.
Peramban modern juga mendukung [Broadcast channel API](mdn:/api/Broadcast_Channel_API), API khusus untuk komunikasi antar-jendela dengan origin yang sama, fiturnya lebih lengkap, tetapi kurang didukung. Ada *libraries* yang melakukan polifill API tersebut, berdasarkan `localStorage`, yang membuatnya tersedia di mana saja.

## Summary
## Ringkasan

Web storage objects `localStorage` and `sessionStorage` allow to store key/value in the browser.
- Both `key` and `value` must be strings.
- The limit is 5mb+, depends on the browser.
- They do not expire.
- The data is bound to the origin (domain/port/protocol).
Objek penyimpanan web `localStorage` dan `sessionStorage` memungkinkan untuk menyimpan *key*/*value* di peramban.
- Baik `key` dan `value` harus berupa string.
- Batasnya adalah 5mb+, tergantung pada peramban.
- Mereka tidak kedaluwarsa.
- Data terikat pada *origin* (domain/port/protokol).

| `localStorage` | `sessionStorage` |
|----------------|------------------|
| Shared between all tabs and windows with the same origin | Visible within a browser tab, including iframes from the same origin |
| Survives browser restart | Survives page refresh (but not tab close) |
| Dibagikan antara semua tab dan window dengan origin yang sama | Terlihat dalam tab browser, termasuk iframe dari origin yang sama |
| Bertahan dari restart peramban | Bertahan dari refresh halaman (tetapi tidak dengan menutup tab) |

API:

- `setItem(key, value)` -- store key/value pair.
- `getItem(key)` -- get the value by key.
- `removeItem(key)` -- remove the key with its value.
- `clear()` -- delete everything.
- `key(index)` -- get the key number `index`.
- `length` -- the number of stored items.
- Use `Object.keys` to get all keys.
- We access keys as object properties, in that case `storage` event isn't triggered.
- `setItem(key, value)` -- menyimpan pasangan *key*/*value*.
- `getItem(key)` -- mendapatkan *value* dengan *key*.
- `removeItem(key)` -- menghapus *key* beserta *value*-nya.
- `clear()` -- menghapus semuanya.
- `key(index)` -- mendapatkan nomor *key* `index`.
- `length` -- jumlah item yang disimpan.
- Gunakan `Object.keys` untuk mendapatkan semua *key*.
- Kita mengakses *key* sebagai properti objek, dalam hal ini *event* `storage` tidak dipicu.

Storage event:
*Event storage*:

- Triggers on `setItem`, `removeItem`, `clear` calls.
- Contains all the data about the operation (`key/oldValue/newValue`), the document `url` and the storage object `storageArea`.
- Triggers on all `window` objects that have access to the storage except the one that generated it (within a tab for `sessionStorage`, globally for `localStorage`).
- Pemicu pada panggilan `setItem`, `removeItem`, `clear`.
- Berisi semua data tentang operasi (`key/oldValue/newValue`), dokumen `url` dan objek penyimpanan `storageArea`.
- Memicu semua objek `window` yang memiliki akses ke penyimpanan kecuali yang membuatnya (dalam tab untuk `sessionStorage`, secara global untuk `localStorage`).