Ты заходишь на Netflix — сайт помнит, что ты выбрал тёмную тему и язык «Русский». Ты закрываешь Notion и открываешь снова — черновик твоей заметки всё ещё там. Всё это без авторизации, без запросов к серверу. Web Storage API позволяет хранить данные прямо в браузере пользователя.
| | localStorage | sessionStorage |
|---|---|---|
| Время жизни | До явного удаления | До закрытия вкладки |
| Вкладки | Общий для всех вкладок | Только текущая вкладка |
| Объём | ~5-10 МБ | ~5 МБ |
| Использование | Настройки, тема, токен | Корзина, форма, сессия |
// Запись
localStorage.setItem('theme', 'dark')
// Чтение (null если ключа нет)
const theme = localStorage.getItem('theme') // 'dark' или null
// Удаление
localStorage.removeItem('theme')
// Полная очистка хранилища
localStorage.clear()localStorage хранит только строки. Для объектов и массивов используй JSON:
const userSettings = {
theme: 'dark',
language: 'ru',
notifications: true,
}
// Сохранение
localStorage.setItem('settings', JSON.stringify(userSettings))
// Загрузка — безопасно через try/catch и null-проверку
function loadSettings() {
const raw = localStorage.getItem('settings')
if (!raw) return { theme: 'light', language: 'ru', notifications: true } // дефолт
try {
return JSON.parse(raw)
} catch {
return {} // повреждённые данные — возвращаем дефолт
}
}Тема приложения:
function setTheme(theme) {
localStorage.setItem('theme', theme)
document.body.dataset.theme = theme // применяем немедленно
}
// При загрузке страницы — восстанавливаем
const savedTheme = localStorage.getItem('theme') ?? 'light'
document.body.dataset.theme = savedThemeТокен авторизации:
// После успешного входа
function login(token, user) {
localStorage.setItem('authToken', token)
localStorage.setItem('user', JSON.stringify(user))
}
// При каждом API-запросе
const token = localStorage.getItem('authToken')
if (token) {
headers['Authorization'] = 'Bearer ' + token
}
// Выход
function logout() {
localStorage.removeItem('authToken')
localStorage.removeItem('user')
}Storage-событие — синхронизация между вкладками:
// Когда одна вкладка меняет localStorage — другие получают событие
window.addEventListener('storage', (e) => {
if (e.key === 'theme') {
document.body.dataset.theme = e.newValue
}
})1. Забыли JSON.parse при чтении объекта:
// Сломано — достаём строку, а не объект:
localStorage.setItem('user', JSON.stringify({ name: 'Иван', age: 25 }))
const user = localStorage.getItem('user') // '{"name":"Иван","age":25}' — строка!
console.log(user.name) // undefined
// Исправлено:
const user = JSON.parse(localStorage.getItem('user'))
console.log(user.name) // 'Иван'2. Не обработали null при отсутствии ключа:
// Сломано — getItem возвращает null, JSON.parse(null) = null:
const settings = JSON.parse(localStorage.getItem('settings')) // null если нет
settings.theme // TypeError: Cannot read properties of null
// Исправлено — проверка на null:
const raw = localStorage.getItem('settings')
const settings = raw ? JSON.parse(raw) : { theme: 'light' }3. Хранят чувствительные данные:
// Никогда не храни в localStorage:
localStorage.setItem('password', userPassword) // плохо — XSS уязвимость!
localStorage.setItem('creditCard', cardNumber) // плохо — читается скриптами!
// Для чувствительных данных — только сервер + httpOnly cookiesКласс StorageService с namespacing и автосериализацией
// Симуляция localStorage для sandbox
const mockStorage = new Map()
const localStorage = {
setItem: (k, v) => mockStorage.set(k, String(v)),
getItem: (k) => mockStorage.get(k) ?? null,
removeItem: (k) => mockStorage.delete(k),
clear: () => mockStorage.clear(),
get length() { return mockStorage.size },
key: (i) => [...mockStorage.keys()][i] ?? null,
}
// Сервис хранилища с пространством имён
class StorageService {
constructor(namespace) {
this.ns = namespace
}
_key(key) { return `${this.ns}:${key}` }
set(key, value) {
try {
localStorage.setItem(this._key(key), JSON.stringify(value))
return true
} catch (e) {
console.error('StorageService.set ошибка:', e.message)
return false
}
}
get(key, defaultValue = null) {
const raw = localStorage.getItem(this._key(key))
if (raw === null) return defaultValue
try {
return JSON.parse(raw)
} catch {
return defaultValue
}
}
remove(key) {
localStorage.removeItem(this._key(key))
}
// Очищаем только ключи нашего namespace
clear() {
const prefix = this.ns + ':'
for (let i = localStorage.length - 1; i >= 0; i--) {
const key = localStorage.key(i)
if (key?.startsWith(prefix)) localStorage.removeItem(key)
}
}
}
// Хранилища для разных частей приложения
const userStorage = new StorageService('user')
const appStorage = new StorageService('app')
// Сохранение данных пользователя
userStorage.set('profile', { name: 'Иван Петров', email: 'ivan@mail.ru', age: 28 })
userStorage.set('preferences', { theme: 'dark', language: 'ru', timezone: 'Europe/Moscow' })
// Сохранение состояния приложения
appStorage.set('lastRoute', '/dashboard/analytics')
appStorage.set('sidebarCollapsed', true)
// Загрузка
const profile = userStorage.get('profile')
console.log('Пользователь:', profile.name) // 'Иван Петров'
console.log('Email:', profile.email) // 'ivan@mail.ru'
const prefs = userStorage.get('preferences')
console.log('Тема:', prefs.theme) // 'dark'
const route = appStorage.get('lastRoute', '/')
console.log('Последняя страница:', route) // '/dashboard/analytics'
const collapsed = appStorage.get('sidebarCollapsed', false)
console.log('Сайдбар свёрнут:', collapsed) // true
// Дефолтное значение если ключа нет
const missing = userStorage.get('nonexistent', { fallback: true })
console.log('Дефолт:', missing.fallback) // trueТы заходишь на Netflix — сайт помнит, что ты выбрал тёмную тему и язык «Русский». Ты закрываешь Notion и открываешь снова — черновик твоей заметки всё ещё там. Всё это без авторизации, без запросов к серверу. Web Storage API позволяет хранить данные прямо в браузере пользователя.
| | localStorage | sessionStorage |
|---|---|---|
| Время жизни | До явного удаления | До закрытия вкладки |
| Вкладки | Общий для всех вкладок | Только текущая вкладка |
| Объём | ~5-10 МБ | ~5 МБ |
| Использование | Настройки, тема, токен | Корзина, форма, сессия |
// Запись
localStorage.setItem('theme', 'dark')
// Чтение (null если ключа нет)
const theme = localStorage.getItem('theme') // 'dark' или null
// Удаление
localStorage.removeItem('theme')
// Полная очистка хранилища
localStorage.clear()localStorage хранит только строки. Для объектов и массивов используй JSON:
const userSettings = {
theme: 'dark',
language: 'ru',
notifications: true,
}
// Сохранение
localStorage.setItem('settings', JSON.stringify(userSettings))
// Загрузка — безопасно через try/catch и null-проверку
function loadSettings() {
const raw = localStorage.getItem('settings')
if (!raw) return { theme: 'light', language: 'ru', notifications: true } // дефолт
try {
return JSON.parse(raw)
} catch {
return {} // повреждённые данные — возвращаем дефолт
}
}Тема приложения:
function setTheme(theme) {
localStorage.setItem('theme', theme)
document.body.dataset.theme = theme // применяем немедленно
}
// При загрузке страницы — восстанавливаем
const savedTheme = localStorage.getItem('theme') ?? 'light'
document.body.dataset.theme = savedThemeТокен авторизации:
// После успешного входа
function login(token, user) {
localStorage.setItem('authToken', token)
localStorage.setItem('user', JSON.stringify(user))
}
// При каждом API-запросе
const token = localStorage.getItem('authToken')
if (token) {
headers['Authorization'] = 'Bearer ' + token
}
// Выход
function logout() {
localStorage.removeItem('authToken')
localStorage.removeItem('user')
}Storage-событие — синхронизация между вкладками:
// Когда одна вкладка меняет localStorage — другие получают событие
window.addEventListener('storage', (e) => {
if (e.key === 'theme') {
document.body.dataset.theme = e.newValue
}
})1. Забыли JSON.parse при чтении объекта:
// Сломано — достаём строку, а не объект:
localStorage.setItem('user', JSON.stringify({ name: 'Иван', age: 25 }))
const user = localStorage.getItem('user') // '{"name":"Иван","age":25}' — строка!
console.log(user.name) // undefined
// Исправлено:
const user = JSON.parse(localStorage.getItem('user'))
console.log(user.name) // 'Иван'2. Не обработали null при отсутствии ключа:
// Сломано — getItem возвращает null, JSON.parse(null) = null:
const settings = JSON.parse(localStorage.getItem('settings')) // null если нет
settings.theme // TypeError: Cannot read properties of null
// Исправлено — проверка на null:
const raw = localStorage.getItem('settings')
const settings = raw ? JSON.parse(raw) : { theme: 'light' }3. Хранят чувствительные данные:
// Никогда не храни в localStorage:
localStorage.setItem('password', userPassword) // плохо — XSS уязвимость!
localStorage.setItem('creditCard', cardNumber) // плохо — читается скриптами!
// Для чувствительных данных — только сервер + httpOnly cookiesКласс StorageService с namespacing и автосериализацией
// Симуляция localStorage для sandbox
const mockStorage = new Map()
const localStorage = {
setItem: (k, v) => mockStorage.set(k, String(v)),
getItem: (k) => mockStorage.get(k) ?? null,
removeItem: (k) => mockStorage.delete(k),
clear: () => mockStorage.clear(),
get length() { return mockStorage.size },
key: (i) => [...mockStorage.keys()][i] ?? null,
}
// Сервис хранилища с пространством имён
class StorageService {
constructor(namespace) {
this.ns = namespace
}
_key(key) { return `${this.ns}:${key}` }
set(key, value) {
try {
localStorage.setItem(this._key(key), JSON.stringify(value))
return true
} catch (e) {
console.error('StorageService.set ошибка:', e.message)
return false
}
}
get(key, defaultValue = null) {
const raw = localStorage.getItem(this._key(key))
if (raw === null) return defaultValue
try {
return JSON.parse(raw)
} catch {
return defaultValue
}
}
remove(key) {
localStorage.removeItem(this._key(key))
}
// Очищаем только ключи нашего namespace
clear() {
const prefix = this.ns + ':'
for (let i = localStorage.length - 1; i >= 0; i--) {
const key = localStorage.key(i)
if (key?.startsWith(prefix)) localStorage.removeItem(key)
}
}
}
// Хранилища для разных частей приложения
const userStorage = new StorageService('user')
const appStorage = new StorageService('app')
// Сохранение данных пользователя
userStorage.set('profile', { name: 'Иван Петров', email: 'ivan@mail.ru', age: 28 })
userStorage.set('preferences', { theme: 'dark', language: 'ru', timezone: 'Europe/Moscow' })
// Сохранение состояния приложения
appStorage.set('lastRoute', '/dashboard/analytics')
appStorage.set('sidebarCollapsed', true)
// Загрузка
const profile = userStorage.get('profile')
console.log('Пользователь:', profile.name) // 'Иван Петров'
console.log('Email:', profile.email) // 'ivan@mail.ru'
const prefs = userStorage.get('preferences')
console.log('Тема:', prefs.theme) // 'dark'
const route = appStorage.get('lastRoute', '/')
console.log('Последняя страница:', route) // '/dashboard/analytics'
const collapsed = appStorage.get('sidebarCollapsed', false)
console.log('Сайдбар свёрнут:', collapsed) // true
// Дефолтное значение если ключа нет
const missing = userStorage.get('nonexistent', { fallback: true })
console.log('Дефолт:', missing.fallback) // trueТы разрабатываешь менеджер настроек для веб-приложения. Используй мок-localStorage (уже написан). Реализуй функцию `createSettingsStore(defaults)`, которая возвращает объект с методами: - `get(key)` — возвращает значение из хранилища или дефолт - `set(key, value)` — сохраняет значение - `reset()` — сбрасывает все настройки к дефолтам Покажи сохранение и восстановление темы, языка и других настроек.
get: return JSON.parse(raw). set: fakeLocalStorage.setItem(PREFIX + key, JSON.stringify(value)). reset: Object.entries(defaults).forEach(([key, value]) => this.set(key, value)).