【Web APIs-Day5】BOM与本地存储

2 阅读5分钟

【Web APIs-Day5】BOM与本地存储

📺 对应视频:P127-P137 | 🎯 核心目标:掌握BOM对象体系、事件循环机制、location/navigator对象与本地存储API


一、BOM 概述

1.1 BOM 是什么?

BOM(Browser Object Model,浏览器对象模型)提供了与浏览器窗口交互的接口。

window(顶层对象,全局作用域)
├── documentDOM 操作
├── location      → URL 信息与跳转
├── navigator     → 浏览器/设备信息
├── history       → 浏览器历史记录
├── screen        → 屏幕信息
└── localStorage / sessionStorage → 本地存储

💡 注意window 是全局对象,所有全局变量和函数都是 window 的属性。alert()setTimeout() 实际上是 window.alert()window.setTimeout()


二、事件循环(Event Loop)

2.1 JS 为什么是单线程?

JS 设计之初是为了操作 DOM,多线程操作同一个 DOM 会产生竞争问题,所以 JS 是单线程的

2.2 同步与异步

// 同步代码:按顺序执行,后面的必须等前面的完成
console.log('1')
console.log('2')
console.log('3')
// 输出:1 2 3

// 异步代码:不阻塞,放到"任务队列"等待
console.log('1')
setTimeout(() => {
  console.log('2')  // 异步,即使延迟为0也在最后执行
}, 0)
console.log('3')
// 输出:1 3 2

2.3 事件循环机制

执行栈(Call Stack)← 同步代码在这里执行
     ↑
     ├── 宏任务队列(Macro Task Queue)
     │   setTimeout / setInterval / I/O / UI渲染
     │
     └── 微任务队列(Micro Task Queue)← 优先于宏任务!
         Promise.then / MutationObserver / queueMicrotask

执行顺序:

  1. 执行同步代码(Call Stack)
  2. 清空全部微任务队列
  3. 取出一个宏任务执行
  4. 再次清空全部微任务队列
  5. 重复 3-4
console.log('1')                    // 同步

setTimeout(() => {
  console.log('2')                  // 宏任务
}, 0)

Promise.resolve().then(() => {
  console.log('3')                  // 微任务
})

console.log('4')                    // 同步

// 输出顺序:1 4 3 2
// 同步先执行,微任务优先于宏任务

三、location 对象

3.1 URL 解析

URL 结构:
https://www.example.com:8080/path/page.html?id=1&name=张三#section2

location.href     → 'https://www.example.com:8080/path/page.html?id=1&name=张三#section2'
location.protocol → 'https:'
location.host     → 'www.example.com:8080'
location.hostname → 'www.example.com'
location.port     → '8080'
location.pathname → '/path/page.html'
location.search   → '?id=1&name=张三'(查询字符串)
location.hash     → '#section2'(锚点)
location.origin   → 'https://www.example.com:8080'

3.2 页面跳转

// 跳转(会留下历史记录,可以回退)
location.href = 'https://juejin.cn'
location.assign('https://juejin.cn')  // 同上

// 替换(不留历史记录,无法回退)
location.replace('https://juejin.cn')

// 重新加载
location.reload()       // 普通刷新
location.reload(true)   // 强制刷新(忽略缓存)

3.3 解析查询参数

// 解析 ?id=1&name=张三
const params = new URLSearchParams(location.search)
params.get('id')      // '1'
params.get('name')    // '张三'
params.has('id')      // true
params.delete('id')

// 遍历所有参数
for (const [key, value] of params) {
  console.log(`${key}: ${value}`)
}

// 将参数传给下一页
const url = new URL('https://example.com/detail')
url.searchParams.set('id', 123)
url.searchParams.set('from', 'home')
location.href = url.toString()
// → https://example.com/detail?id=123&from=home

四、navigator 对象

// 浏览器信息
navigator.userAgent    // 用户代理字符串(识别浏览器/OS)
navigator.language     // 浏览器语言,如 'zh-CN'
navigator.onLine       // 是否联网(true/false)
navigator.platform     // 操作系统平台
navigator.cookieEnabled // 是否启用 Cookie

// 判断移动端
function isMobile() {
  return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent)
}

// 根据设备跳转不同页面
if (isMobile()) {
  location.href = '/m/index.html'  // 移动版
} else {
  location.href = '/pc/index.html' // PC版
}

// 获取地理位置(需要用户授权)
navigator.geolocation.getCurrentPosition(
  position => {
    const { latitude, longitude } = position.coords
    console.log(`纬度:${latitude},经度:${longitude}`)
  },
  error => {
    console.log('获取位置失败:', error.message)
  }
)

五、history 对象

history.back()           // 后退(等同于浏览器返回按钮)
history.forward()        // 前进
history.go(-1)           // 后退1步
history.go(1)            // 前进1步
history.go(0)            // 刷新

// 操作历史记录(SPA 路由的基础)
history.pushState({ page: 1 }, '', '/page1')    // 添加历史记录,URL变化但不刷新
history.replaceState({ page: 2 }, '', '/page2') // 替换当前历史记录

// 监听前进/后退
window.addEventListener('popstate', e => {
  console.log('历史变化:', e.state)
})

六、本地存储

6.1 localStorage(持久化存储)

// 特点:
// - 容量约 5MB
// - 不会过期,除非手动清除
// - 同域名下共享(跨 tab 页可访问)
// - 只能存字符串!

// 存储
localStorage.setItem('username', '张三')
localStorage.setItem('age', 18)  // 数字自动转字符串

// 读取
localStorage.getItem('username')  // '张三'
localStorage.getItem('age')       // '18'(字符串!)

// 删除
localStorage.removeItem('username')

// 清空所有
localStorage.clear()

// 存储对象(必须序列化)
const user = { name: '张三', age: 18 }
localStorage.setItem('user', JSON.stringify(user))   // 存
const savedUser = JSON.parse(localStorage.getItem('user'))  // 取

// 封装工具函数
const storage = {
  set(key, value) {
    localStorage.setItem(key, JSON.stringify(value))
  },
  get(key) {
    const val = localStorage.getItem(key)
    return val ? JSON.parse(val) : null
  },
  remove(key) {
    localStorage.removeItem(key)
  }
}

6.2 sessionStorage(会话存储)

// 特点:
// - 容量约 5MB
// - 关闭 tab 页即清除
// - 仅限当前 tab(不跨标签页)
// - 也只能存字符串

// API 与 localStorage 完全相同
sessionStorage.setItem('token', 'abc123')
sessionStorage.getItem('token')
sessionStorage.removeItem('token')
sessionStorage.clear()

6.3 localStorage vs sessionStorage vs Cookie

特性localStoragesessionStorageCookie
容量~5MB~5MB~4KB
过期时间永久关闭标签清除可设置
随请求发送✅(自动)
跨 tab
JS 可访问可设置限制

七、综合案例

案例:记住用户设置

// 读取用户保存的主题
const savedTheme = localStorage.getItem('theme') || 'light'
document.body.className = savedTheme

// 切换主题并保存
document.querySelector('.theme-toggle').addEventListener('click', () => {
  const current = document.body.className
  const newTheme = current === 'light' ? 'dark' : 'light'
  document.body.className = newTheme
  localStorage.setItem('theme', newTheme)
})

案例:购物车持久化

// 读取购物车
function getCart() {
  return JSON.parse(localStorage.getItem('cart')) || []
}

// 添加商品
function addToCart(product) {
  const cart = getCart()
  const existing = cart.find(item => item.id === product.id)
  if (existing) {
    existing.quantity++
  } else {
    cart.push({ ...product, quantity: 1 })
  }
  localStorage.setItem('cart', JSON.stringify(cart))
}

八、知识图谱

BOM 与本地存储
├── BOM 对象体系
│   └── window 顶层:location / navigator / history / screen
├── 事件循环
│   ├── 同步代码(调用栈)
│   ├── 微任务(Promise.then)← 优先
│   └── 宏任务(setTimeout)← 后执行
├── location
│   ├── href / pathname / search / hash
│   ├── assign / replace(跳转)
│   └── URLSearchParams(解析查询参数)
├── navigator
│   ├── userAgent(识别设备/浏览器)
│   └── geolocation(获取位置)
├── history
│   ├── back / forward / go
│   └── pushState / replaceState(SPA路由)
└── 本地存储
    ├── localStorage(持久):setItem/getItem/removeItem
    ├── sessionStorage(会话):同上
    └── 注意:只能存字符串,对象需 JSON 序列化

九、高频面试题

Q1:宏任务和微任务的区别?哪些属于微任务?

微任务:Promise.thenMutationObserverqueueMicrotask;宏任务:setTimeoutsetIntervalrequestAnimationFrame、UI 渲染。同步代码完成后,先清空全部微任务,再取一个宏任务执行,如此循环。

Q2:localStorage 和 sessionStorage 的区别?

两者 API 完全相同,区别在于:localStorage 数据永久保存,关闭浏览器后仍然存在,且多个 tab 共享;sessionStorage 在关闭 tab 页后清除,且不跨 tab。

Q3:如何跨页面传递数据?

① URL 参数(?id=1):小量数据;② localStorage:大量数据持久化;③ sessionStorage:临时数据;④ BroadcastChannel:实时跨 tab 通信;⑤ 后端存储(推荐重要数据)。


⬅️ 上一篇Web APIs Day4 - 节点操作 ➡️ 下一篇Web APIs Day6&7 - 正则与综合实战