详细讲解JavaScript中的localStorage与sessionStorage本地存储API,包含字符串与JSON对象读写、删除、遍历的完整代码示例,以及两者在生命周期、作用域与异常处理上的关键区别。
JavaScript本地存储示例:localStorage与sessionStorage读写操作
时间:2026-10-30
浏览:612
文章目录
localStorage 与 sessionStorage 是最标准、最直接的实现方式。本文通过清晰易懂的代码实例,逐一讲解两者的核心读写、删除操作、对象序列化转换技巧,并结合具体对比剖析它们的使用场景与避坑要点。
一、核心概念:localStorage 与 sessionStorage 的定位与差异
localStorage 与 sessionStorage 同属于 W3C Web Storage 规范。它们均以键值对(Key-Value)形式将数据存储在用户的本地浏览器中,且每个同源域名下通常拥有约 5MB 的存储限额。二者最大的区别在于生命周期与作用范围:
- localStorage:持久化存储。写入的数据不会随网页关闭或浏览器退出而失效,除非用户主动清理浏览器缓存或通过 JavaScript 代码显式删除,否则数据永久有效;在同一个浏览器内,相同协议、域名和端口的所有标签页与窗口均可共享该数据。
- sessionStorage:会话级存储。生命周期严格限定在当前浏览器标签页(或窗口)的单次会话内。一旦该标签页关闭,对应的数据便被彻底清除;即使在同一浏览器中打开相同的网址,两个互不关联的标签页也拥有相互隔离的 sessionStorage。
图 1:localStorage 与 sessionStorage 的体系结构、生命周期及隔离特性对比
二、基础读写:Storage 接口的标准操作方法
在 JavaScript 环境中,localStorage 与 sessionStorage 实现了完全相同的 Storage 接口。这意味着二者的调用语法完全一致,掌握一套 API 即可通用:
| 方法 / 属性 | 调用方式 | 功能说明 |
|---|---|---|
setItem() |
storage.setItem(key, value) |
存储键值对。若已存在该 key,则覆盖其旧值。 |
getItem() |
storage.getItem(key) |
读取对应 key 的字符串值;若该 key 不存在,返回 null。 |
removeItem() |
storage.removeItem(key) |
根据 key 移除指定的数据项。 |
clear() |
storage.clear() |
清空当前 storage 中的所有键值对。 |
key() |
storage.key(index) |
按索引序号获取存储项的 key 名称。 |
length |
storage.length |
只读属性,返回当前存储的键值对总数量。 |
图 2:Web Storage 从存入、校验、消费到清理的完整操作步骤流水线
1. localStorage 读写实操示例
以保存用户的暗色模式设置(theme)为例,以下代码演示基础写入、读取、删除及清空操作:
// 1. 写入数据(键与值必须为字符串或可隐式转换为字符串)
localStorage.setItem('site_theme', 'dark');
localStorage.setItem('user_lang', 'zh-CN');
// 2. 读取数据
const currentTheme = localStorage.getItem('site_theme');
console.log('当前主题偏好:', currentTheme); // 输出:"dark"
// 3. 读取不存在的键
const notFound = localStorage.getItem('non_existent_key');
console.log(notFound); // 输出:null
// 4. 删除单个数据项
localStorage.removeItem('user_lang');
// 5. 查看当前存储项总数
console.log('存储条目数:', localStorage.length);
// 6. 清空当前域下的所有 localStorage 记录
localStorage.clear();
2. sessionStorage 会话读写实操示例
在多步骤表单或草稿录入场景下,通常只需要在用户当前标签页未关闭前临时记录数据,避免意外刷新导致输入丢失,此时使用 sessionStorage 是最理想的方案:
// 1. 记录第一步表单填写的临时邮箱草稿
sessionStorage.setItem('draft_email', 'alex@example.com');
sessionStorage.setItem('step_progress', '2');
// 2. 用户刷新页面后恢复步骤
const savedStep = sessionStorage.getItem('step_progress');
if (savedStep !== null) {
console.log(`恢复至第 ${savedStep} 步继续填写`);
}
// 3. 表单提交完毕后,清理草稿
sessionStorage.removeItem('draft_email');
sessionStorage.removeItem('step_progress');
三、进阶处理:复杂对象与数组的序列化存储
Web Storage 的底层规范仅支持存储 DOMString(字符串)。如果直接将 JavaScript 对象或数组传入 setItem(),浏览器会强制调用对象的 .toString() 方法,最终保存的结果是无意义的 "[object Object]",导致数据无法还原。
要存储对象、数组或布尔/数字复合结构,标准的做法是在存储前使用 JSON.stringify() 序列化为 JSON 字符串,读取后再通过 JSON.parse() 解析还原。
图 3:JavaScript 对象存储时的类型转换机制与 JSON 序列化标准方案
健壮的封装函数示例
为了避免每次读取时手动编写 JSON.parse 并防止因脏数据导致的解析崩溃,建议在项目中封装统一的读写辅助工具函数:
const storageHelper = {
/**
* 写入数据(支持基础类型与复杂对象)
*/
set(key, value, isSession = false) {
try {
const targetStorage = isSession ? sessionStorage : localStorage;
// 将任意数据序列化为 JSON 字符串
const serialized = JSON.stringify(value);
targetStorage.setItem(key, serialized);
return true;
} catch (error) {
// 捕获超出存储配额或浏览器隐私模式限制的异常
console.error(`存储键 "${key}" 失败:`, error);
return false;
}
},
/**
* 读取数据(自动反序列化,支持缺省兜底值)
*/
get(key, defaultValue = null, isSession = false) {
const targetStorage = isSession ? sessionStorage : localStorage;
const rawValue = targetStorage.getItem(key);
// 键不存在时直接返回兜底默认值
if (rawValue === null) {
return defaultValue;
}
try {
return JSON.parse(rawValue);
} catch (err) {
// 若历史遗留数据是普通字符串而非合法 JSON,降级返回原文本
return rawValue;
}
},
/**
* 移除指定项
*/
remove(key, isSession = false) {
const targetStorage = isSession ? sessionStorage : localStorage;
targetStorage.removeItem(key);
}
};
// 使用示例:保存和读取复杂的配置对象
storageHelper.set('user_profile', {
id: 10086,
name: '张三',
roles: ['admin', 'editor'],
settings: { notifications: true }
});
const profile = storageHelper.get('user_profile', {});
console.log(profile.roles[0]); // 正常输出: "admin"
四、遍历与键管理操作
当需要审查当前应用存入了哪些数据,或根据特定前缀进行批量清理时,可以通过 length 属性与 key(index) 索引方法配合遍历:
// 遍历当前 localStorage 中的全部键值对
for (let i = 0; i < localStorage.length; i++) {
const storageKey = localStorage.key(i);
const storageValue = localStorage.getItem(storageKey);
console.log(`索引 [${i}] -> 键: ${storageKey}, 值: ${storageValue}`);
}
// 实用技巧:批量清理特定业务前缀的临时缓存项(如 'cache_')
const keysToRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const keyName = localStorage.key(i);
if (keyName && keyName.startsWith('cache_')) {
keysToRemove.push(keyName);
}
}
// 遍历完成后再统一删除,避免在边遍历边删除时引起索引位移混乱
keysToRemove.forEach(key => localStorage.removeItem(key));
五、跨标签页通信:storage 事件监听
当在同一浏览器的另一个同源标签页中修改或删除了 localStorage 的数据时,当前标签页的 window 对象会触发 storage 事件。这一机制常用于实现轻量级的跨窗口状态同步(例如用户在 A 标签页退出登录后,B 标签页立刻感知并同步跳转):
// 监听外部窗口对 localStorage 的变动
window.addEventListener('storage', (event) => {
console.log('被修改的存储键:', event.key);
console.log('原旧值:', event.oldValue);
console.log('新更新值:', event.newValue);
console.log('触发变动的页面 URL:', event.url);
if (event.key === 'site_theme') {
// 同步修改当前标签页的主题显示
document.body.className = event.newValue;
}
});
storage 事件只在产生变动窗口之外的其他同源窗口中被触发;在发起 setItem 或 removeItem 的本页面内,默认是不会触发自身的 storage 事件的。
六、关键选型维度与避坑注意事项
在实际项目落地时,选用本地存储方案需围绕数据安全、容量规划以及生命周期这三个核心维度进行决策:
图 4:Web Storage 核心选型决策树、性能边界与前端安全防御建议
- 主线程同步阻塞:
localStorage与sessionStorage的读写是完全同步执行的。如果在循环中高频读写或操作几十兆的复杂对象,会阻塞 UI 渲染导致页面掉帧甚至卡死。超大数据存储需求应选用异步的IndexedDB。 - 防御 XSS 攻击:存储在本地的任何数据对本页面运行的所有 JavaScript 均完全公开。如果网页存在跨站脚本(XSS)漏洞,攻击者可轻易通过
localStorage.getItem()窃取数据。因此,切勿在本地存储中保存明文密码、银行卡信息或无防护的长期身份凭据。 - 做好容量异常保护:各浏览器普遍为单域名分配约 5MB 左右配额。当存储空间耗尽时,
setItem会直接抛出QuotaExceededError异常。生产级代码应统一包裹try...catch并设计合理的旧缓存淘汰机制。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。