博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JS沙雕问题2:localStorage进程安全吗?

Posted on 2021-10-18 14:59  kpi311  阅读(197)  评论(0编辑  收藏  举报

0 tl;dr

如果程序的需求有切换页面时保存状态到localStorage的话,请确保自己的程序进程安全。浏览器似乎没有对localStorage加锁。

1 问题原因

这两天学react的时候用到了localStorage。恰逢又有有同学讲到了进程和线程。

一个问题从脑海中蹦了出来:多进程的浏览器如何管理这一块看起来像是“共享内存”的存储空间?

1.1 前置知识:什么是localStorage

MDN的说法

简而言之:

  1. window的属性
  2. 能够长期存储在本地硬盘(关闭浏览器后还存在)
  3. 同一个域名下的页面都能够共享同一份localStorage

1.2 发现的问题:“共享内存”

现代浏览器每个标签页就是一个子进程。而同一个域名的页面都能访问到硬盘上的同一个对象,那么经典问题就来了:数据一致性如何保证?

2 分析

万事不决问谷歌。先看看有没有人和我想得差不多

确实有人发现了这个问题:同步读写一致性无法保证。

回答的哥们儿去WHATWG的文档看了一圈发现语焉不详,标准并没有规范浏览器的具体实现。

而浏览器为了效率一般会保存一份缓存,这就会引起不一致。

3 验证

理论有了,那么着手验证一把。

3.1 实验设计

一个简单的思路是:页面获得焦点时,读取数据并展示;页面失去焦点时,重复多次写入数据以制造不一致的机会。

那么准备两个页面,这两个页面失焦时分别对同一个key写入不同的数据,激活时读取该key的值,验证读取和写入的情况是否一致。

3.2 代码

先express起个静态服务器:

// app.js

const express = require('express')

const app = express()

app.use(express.static('./'))

app.listen(3000, () => {
    console.log('Server Listening at http://127.0.0.1:3000')
})

然后在同目录下,建两个html文档index.htmlpage2.html:

其中,index.html从0-9999更新10000次数据库,写入一个对象,其值递增。

<!-- index.html -->
<body>
    <div></div>
    <script>
        const div = document.querySelector('div')
        document.addEventListener('visibilitychange', () => {
            if(document.hidden){
                for(let i = 0;i< 10000;i+=1){
                    localStorage.setItem('abkey', JSON.stringify({abk: i}))
                }
            }else{
                const v = localStorage.getItem('abkey')
                if(v) div.innerHTML = `本次数据为:${JSON.parse(v).abk},原始数据为:${v}`
                else div.innerHTML = '暂未存储数据'
            }
        })
    </script>
</body>

page2.html从0000-29999更新10000次数据库,写入一个对象,其值递增。

<!-- page2.html -->
<body>
    <div></div>
    <script>
        const div = document.querySelector('div')
        document.addEventListener('visibilitychange', () => {
            if(document.hidden){
                for(let i = 20000;i< 30000;i+=1){
                    localStorage.setItem('abkey', JSON.stringify({abk: i}))
                }
            }else{
                const v = localStorage.getItem('abkey')
                if(v) div.innerHTML = `本次数据为:${JSON.parse(v).abk},原始数据为:${v}`
                else div.innerHTML = '暂未存储数据'
            }
        })
    </script>
</body>

3.3 实验结果

可见,localStorage并不存在一个写入锁去保护数据的一致性。(毕竟不是数据库)

4 总结

不应当意念式编程——程序员应当保证自己程序的正确性,而不是假设自己依赖的基础设施的行为和自己的想象相同。