你可能不知道的localStorage

2,258 阅读3分钟

前言

最近在给项目做一些优化,其中有一项就是提取和封装一些常用的公共方法。于是便想到了给localStorage(本地存储)进行一次封装。

localStorage是什么?

localStorage是一个用来做本地持久化存储的Web Api。localStorage以键值对的形式存储数据。它的常用方法如下:

// 设置
localStorage.setItem(key, val)
// 获取
const val = localStorage.getItem(key)
// 移除
localStorage.removeItem(key)
// 清空
localStorage.clear()

具体有哪些作用在这里就不过多介绍。

封装localStorage

localStorage的 Api 都已经比较简洁实用了, 那还有什么好封装的呢?我的想法主要是为了解决以下几点:

  • 设置/获取时可能出现的意外错误
  • 设置/获取的value值都是字符串,不方便直接使用。
  • 没有像cookie一样的过期时间限制

好了,不多说,直接撸代码:

const ls = window.localStorage
// 设置
const setItem = (key, val, expired) => {
// expired参数为毫秒数
  try {
    const data = {
      [key]: val,
    }
    if (expired) {
      data[`__${key}__expired`] = Date.now() + expired
    }
    ls.setItem(key, JSON.stringify(data))
  } catch (e) {
    console.log('setItem error', e)
  }
}
// 获取
const getItem = key => {
  try {
    const data = JSON.parse(ls.getItem(key))
    if (!data) return
    if (data.hasOwnProperty(`__${key}__expired`)) {
      if (data[`__${key}__expired`] >= Date.now()) {
        return data[key]
      }
      removeItem(key)
      return
    }
    return data[key]
  } catch (e) {
    console.log('getItem error', e)
  }
}
// 移除
const removeItem = key => {
  ls.removeItem(key)
}
// 清空
const clear = () => {
  ls.clear()
}

export default { setItem, getItem, removeItem, clear }

代码不多解释,没有什么复杂的逻辑

你可能不知道的localStorage

我们都知道localStorage是HTML5 Web存储的一部分,HTML5 Web 存储是以键/值对的形式存储的,那键/值对的集合不就是对象Object吗?localStorageObject有什么关系呢??

由上图我们知道原来localStorage继承Storage, 而Storage又是继承Object。它们的原型链关系是:

localStorage.__proto__ === Storage.prototype

Storage.prototype.__proto__ === Object.prototype
  • 既然继承于Object,那肯定可以使用一些对象的方法
// 可以使用hasOwnProperty判断键是否被储存
localStorage.hasOwnProperty('name') === true
localStorage.hasOwnProperty('name1') === false
  • 可以使用Object.keys得到键的可遍历集合
Object.keys(localStorage) // ["b", "name", "age", "a"]
  • localStorage存在length属性,表示存储键/值的数量
// 数量
localStorage.length // 2 
  • 可以使用点语法.或者中括号[]的形式访问localStorage中存储的值吗?
// 设置
localStorage.name = 'zhangsan' 
localStorage['age'] = 19 

// 获取
localStorage.name // 'zhangsan'
localStorage.age // '19'

那既然可以直接使用点语法.或者中括号[]的形式,为什么还需要setItem、getItem呢?继续探索。

const str = 'string'
const obj = { obj: 'obj'}
const s = Symbol('s')

localStorage.setItem(str,'string1') // string => 'string1'
localStorage[str] = 'string2' // string => 'string2'
// 相同键名出现覆盖

localStorage.setItem(obj,'obj1') // [object Object] => 'obj1'
localStorage[obj] = 'obj2' // [object Object] => 'obj2'
localStorage.setItem(obj,obj) // [object Object] => '[object Object]'
localStorage[obj] = obj // [object Object] => '[object Object]'
// 相同键名出现覆盖

localStorage.setItem(s,'symbol') // TypeError: Cannot convert a Symbol value to a string
localStorage[s] = 'symbol' // Symbol(s) => 'symbol'

这里我们知道setItem(key,val)方法中的key、val必须为字符串或者可以被转换成字符串的其他类型。
当key为symbol类型时,使用setItem()设置失败,但是使用中括号[]设置时成功了。

那到底成功了吗?

localStorage.getItem(s) // TypeError: Cannot convert a Symbol value to a string
// 这里的错误应该还是 s 不能被转换成字符串的原因。


localStorage[s] // "symbol"
// why?localStorage的键不是应该只能为字符串类型吗?

陷入疑惑中...

我们尝试查看一下localStorage,如下图:

从打印结果来看,Symbol(s)键存在于localStorage对象中,但是length属性却没有因为增加了Symbol(s)键而改变。

再查看一下浏览器的Application:

为什么没有Symbol(s)键???
脑子里突然有一个想法,刷新一次浏览器看看。

哇哇哇。。。
我似乎明白了

总结

对象的键可以为Symbol类型,localStorage也是一个对象。
因此可以使用中括号的方式给localStorage添加Symbol类型的键。

但是localStorage存在的意义是什么?持久化储存!Symbol类型的键却做不到持久化,可能就是官方推荐使用setItem、getItem接口的原因吧!