Serialization
Last assignment, we were able to get a single item added to the shopping cart, but of course most people will want to order multiple items. We chose the scope of that assignment carefully, though, as generalizing from one to many items bumps into the limitations of the Storage API we're using for this purpose.
Format Constraints
The natural inclination to store multiple items is to put them in an array. I went ahead and tried this in the console:
$ localStorage.setItem("item_ids", [1, 2, 3])
undefined
All good, right? Guess again, this is one of the rough edges with Javascript's permissive nature. Check out what it looks like when I pull it back out:
$ localStorage.getItem("item_ids")
"1,2,3"
So what actually got saved to local storage is a string, and that's because the only thing that can be saved to local storage is strings. I'm not sure if you ran into this in the assignment or it got resolved invisibly, but even numbers get converted to strings when they're being saved:
$ localStorage.setItem("item_id", 1)
undefined
$ localStorage.getItem("item_id")
"1"
To understand what's going on, check out the toString method, which is defined on
every object in Javascript.
$ [1, 2, 3].toString()
"1,2,3"
$ (5).toString()
"5"
$ "string".toString()
"string"
Those objects turn into somewhat reasonable strings, but the default behavior is a lot less useful:
$ let item = {item_id: 5}
undefined
$ item.toString()
"[object Object]"
But, if you define a function, localStorage will have no problems using it:
$ item = {item_id: 5, toString: function() { return `item_id: ${this.item_id}` }}
Object { item_id: 5, toString: toString() }
$ item.toString()
"item_id: 5"
$ localStorage.setItem("item", item)
undefined
$ localStorage.getItem("item")
"item_id: 5"
Serialization
This process of taking a data structure and converting it into a string is known as Serialization. The toString method is relatively workable in simple situations, as seen in the
last assignment, but it can quickly get difficult to manage.
To illustrate this, let's return to the stringified version of an array:
$ let x = ["1", "2", "3"]
undefined
$ x.toString()
"1,2,3"
$ x.toString().split(",")
Array(3) [ "1", "2", "3" ]
But what happens if a string containing a comma appears in the array?
$ x = ["1", "2,3"]
Array [ "1", "2,3" ]
$ x.toString()
"1,2,3"
$ x.toString().split(",")
Array(3) [ "1", "2", "3" ]
As you can see, the basic string conversion tooling can become insufficient rather quickly. The standard the industry has settled on is to convert to and from JSON. Javascript provides some basic facilities to do this (it stands for JavaScript Object Notation, after all):
JSON.stringify(x)
'["1","2,3"]'
JSON.parse(JSON.stringify(x))
Array [ "1", "2,3" ]
Furthermore, it's capable of serializing and deserializing simple objects without the need for a custom method:
$ JSON.stringify({ item_id: 5 })
'{"item_id":5}'
It's not a complete panacea, though, custom classes will have their information stripped away in the process:
$ class Item {
$ constructor(item_id) {
$ this.item_id = item_id
$ }
$ }
undefined
$ JSON.stringify(new Item(5))
'{"item_id":5}'
There are different techniques and libraries to help deal with this problem, but often times they just make things too complicated. For our simple use case, for example, the facilities available are more than enough.