JavaScript:BOM 详解

JavaScript BOM 详解:从基础概念到 React 实战

适合人群:有后端开发经验,正在学习 JavaScript、React、TypeScript 的开发者。

学习目标:理解 BOM 的作用,掌握 window、location、history、navigator、浏览器存储和定时器等常用 API。

一、什么是 BOM?

BOM 全称 Browser Object Model(浏览器对象模型) 。

简单理解:

BOM 是 JavaScript 访问和操作浏览器窗口及浏览器功能的一组对象和 API。

之前学习过 DOM,可以这样区分:

对比 DOM BOM
全称 Document Object Model Browser Object Model
主要作用 操作网页内容 访问浏览器功能
核心对象 document window
典型操作 修改 HTML、操作节点 页面跳转、历史记录、存储
示例 document.querySelector() window.location.href

例如:

javascript 复制代码
// DOM:修改页面内容
document.querySelector("h1").textContent = "Hello";

// BOM:获取当前网页地址
console.log(window.location.href);

一句话记忆:

DOM 管网页内容,BOM 管浏览器功能。

需要注意,BOM 是一个历史上常用的统称,并不是像 DOM 那样独立定义的统一标准。


二、BOM 的核心对象:window

window 是浏览器页面中的全局对象,也是学习 BOM 最重要的入口。

它提供或关联了很多常用功能:

bash 复制代码
window
├── document         # DOM 文档
├── location         # 当前 URL、页面跳转
├── history          # 浏览器历史记录
├── navigator        # 浏览器环境信息
├── screen           # 屏幕信息
├── localStorage     # 本地持久化存储
├── sessionStorage   # 会话存储
├── setTimeout()     # 延迟执行
├── setInterval()    # 定时重复执行
├── alert()          # 提示弹窗
└── innerWidth       # 浏览器视口宽度

1. window 的基本使用

javascript 复制代码
// 浏览器视口宽度
console.log(window.innerWidth);

// 浏览器视口高度
console.log(window.innerHeight);

// 弹出提示框
window.alert("Hello BOM");

// 当前网页地址
console.log(window.location.href);

2. window 为什么可以省略?

例如:

javascript 复制代码
window.alert("Hello");

通常也可以写成:

scss 复制代码
alert("Hello");

因为浏览器中的许多全局函数和属性都可以通过 window 访问。

类似地:

dart 复制代码
window.document === document; // true

重点: window 是浏览器的全局对象,而 document 是它的一个属性。


三、location:URL 与页面跳转(P0)

location 是工作中最常用的浏览器 API 之一。

假设当前页面地址:

bash 复制代码
https://example.com:8080/tasks/list?id=123&status=running#detail

可以这样获取 URL 信息:

javascript 复制代码
console.log(window.location.href);
// 完整 URL

console.log(window.location.protocol);
// https:

console.log(window.location.hostname);
// example.com

console.log(window.location.port);
// 8080

console.log(window.location.pathname);
// /tasks/list

console.log(window.location.search);
// ?id=123&status=running

console.log(window.location.hash);
// #detail

1. URL 参数解析

实际开发中,经常需要获取 URL 上的查询参数。

例如:

bash 复制代码
https://example.com/tasks?id=123&status=running

代码:

csharp 复制代码
const params = new URLSearchParams(
  window.location.search
);

const taskId = params.get("id");
const status = params.get("status");

console.log(taskId); // "123"
console.log(status); // "running"

注意:URLSearchParams.get() 返回字符串或 null。

2. 页面跳转

javascript 复制代码
// 方式一:直接赋值
window.location.href = "/login";

// 方式二:跳转到新地址
window.location.assign("/login");

// 方式三:替换当前历史记录
window.location.replace("/login");

// 刷新当前页面
window.location.reload();

其中 assign() 和 replace() 的区别:

  • assign():正常导航,会产生相应的历史记录。
  • replace():替换当前历史记录条目,返回时通常不会回到被替换的页面。

业务场景:

javascript 复制代码
function logout() {
  // 清理登录相关状态...

  window.location.replace("/login");
}

四、history:浏览器历史记录(P1)

history 用来操作浏览器的会话历史记录。

1. 前进与后退

javascript 复制代码
// 返回上一页
window.history.back();

// 前进一页
window.history.forward();

// 后退两步
window.history.go(-2);

2. 修改 URL,但不刷新页面

javascript 复制代码
window.history.pushState(
  { taskId: 123 },
  "",
  "/tasks/detail/123"
);

执行后,浏览器地址栏会变化,但不会因为 pushState 自动重新加载页面。

也可以使用:

javascript 复制代码
window.history.replaceState(
  { taskId: 123 },
  "",
  "/tasks/detail/123"
);

区别:

  • pushState():增加一个历史记录条目。
  • replaceState():替换当前历史记录条目。

注意:这两个方法通常要求目标 URL 与当前页面同源,而且修改 URL 本身不会自动更新页面内容。

3. React Router 和 history 的关系

在 React 单页应用(SPA)中:

javascript 复制代码
import { useNavigate } from "react-router";

function TaskPage() {
  const navigate = useNavigate();

  return (
    <button onClick={() => navigate("/tasks")}>
      查看任务
    </button>
  );
}

React Router 可以利用浏览器 History API 管理路由导航与页面渲染。

实际 React 开发中,应用内部跳转通常优先使用路由库,而不是直接操作 window.location。


五、localStorage 与 sessionStorage(P0)

浏览器提供了两种常用的客户端存储 API。

对比 localStorage sessionStorage
保存位置 浏览器本地 浏览器会话存储
刷新页面 通常保留 通常保留
关闭标签页 通常保留 通常清除
数据类型 字符串 字符串
常见用途 主题、用户偏好 单标签页临时状态

1. localStorage

javascript 复制代码
// 存储
localStorage.setItem("theme", "dark");

// 读取
const theme = localStorage.getItem("theme");

console.log(theme); // dark

// 删除指定数据
localStorage.removeItem("theme");

// 清空当前源的全部 localStorage
localStorage.clear();

注意:localStorage 主要用于保存非敏感的客户端数据,不建议直接存储密码或敏感身份凭证。

2. 保存对象

Storage 只能直接存储字符串。

如果需要保存对象:

javascript 复制代码
const user = {
  name: "Kevin",
  age: 25
};

// 保存对象
localStorage.setItem(
  "user",
  JSON.stringify(user)
);

// 读取对象
const raw = localStorage.getItem("user");

const savedUser = raw ? JSON.parse(raw) : null;

console.log(savedUser?.name);

这里涉及两个方法:

  • JSON.stringify():对象转 JSON 字符串。
  • JSON.parse():JSON 字符串转对象。

实际业务中,读取不可信或可能损坏的数据时,应通过 try/catch 处理 JSON 解析错误。

3. sessionStorage

使用方式基本相同:

ini 复制代码
sessionStorage.setItem("taskId", "123");

const taskId = sessionStorage.getItem("taskId");

sessionStorage.removeItem("taskId");

通常用于标签页会话期间的临时数据。


六、定时器:setTimeout 与 setInterval(P0)

1. setTimeout:延迟执行一次

javascript 复制代码
setTimeout(() => {
  console.log("2秒后执行");
}, 2000);

这里:

  • 第一个参数:回调函数。
  • 第二个参数:延迟时间,单位毫秒。

可以取消:

javascript 复制代码
const timer = setTimeout(() => {
  console.log("执行任务");
}, 2000);

clearTimeout(timer);

2. setInterval:定时重复执行

javascript 复制代码
const timer = setInterval(() => {
  console.log("定时检查任务状态");
}, 5000);

表示每隔大约 5 秒执行一次回调。

停止定时器:

scss 复制代码
clearInterval(timer);

3. 实际场景:任务状态轮询

例如每隔 5 秒查询一次任务状态:

javascript 复制代码
async function getTaskStatus() {
  const response = await fetch("/api/task/status");

  if (!response.ok) {
    throw new Error("查询失败");
  }

  return response.json();
}

const timer = setInterval(async () => {
  try {
    const data = await getTaskStatus();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}, 5000);

注意:如果一次请求超过 5 秒,setInterval 可能产生请求重叠。

实际项目中可以考虑在上一次请求结束后再通过 setTimeout 安排下一次轮询,或者使用 SWR、TanStack Query 等数据请求工具。


七、navigator:获取浏览器环境信息(P1)

navigator 提供浏览器运行环境相关的信息。

例如:

arduino 复制代码
// 浏览器是否报告在线
console.log(navigator.onLine);

// 浏览器语言
console.log(navigator.language);

// 用户代理信息
console.log(navigator.userAgent);

实际使用场景:

  • 获取浏览器语言。
  • 判断是否可能处于离线状态。
  • 访问剪贴板等浏览器功能。

例如:

csharp 复制代码
await navigator.clipboard.writeText("任务ID:123");

这会尝试向剪贴板写入文本。

注意:剪贴板 API 通常需要安全上下文(HTTPS 等),并受到浏览器权限和用户交互限制。

另外,navigator.onLine 不能可靠证明业务后端接口一定可访问。


八、React 中如何使用 BOM?

React 本质上仍然运行在 JavaScript 环境中,因此可以使用浏览器 API。

例如:

javascript 复制代码
import { useEffect, useState } from "react";

function WindowInfo() {
  const [width, setWidth] = useState(
    () => window.innerWidth
  );

  useEffect(() => {
    function handleResize() {
      setWidth(window.innerWidth);
    }

    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  return <div>窗口宽度:{width}px</div>;
}

核心流程:

  1. 通过 window.innerWidth 获取窗口宽度。
  2. 通过 addEventListener() 监听窗口变化。
  3. 使用 setWidth() 更新 React 状态。
  4. React 根据新状态重新渲染页面。
  5. 在 useEffect 清理函数中解除监听。

注意:上面示例适用于浏览器客户端渲染。如果项目涉及 SSR,初始渲染时可能没有 window,需要额外处理。


九、常见问题

1. window 和 document 有什么区别?

window 是浏览器页面的全局对象。

document 表示当前 HTML 文档,提供 DOM 操作能力。

例如:

dart 复制代码
window.location.href; // BOM
document.querySelector("div"); // DOM

2. BOM 和 DOM 是独立的吗?

概念上可以区分,但实际 API 之间有联系。

例如:

dart 复制代码
window.document

就是通过 window 访问 DOM 文档。

3. BOM 和 Node.js 有什么区别?

Node.js 并不是浏览器环境。

例如:

javascript 复制代码
console.log(window);

在普通 Node.js 环境中,window 通常不存在。

Node.js 和浏览器都支持 JavaScript,但它们提供的运行环境 API 不完全一样。

4. React 中需要直接操作 window 吗?

需要,但建议根据场景选择:

  • 操作 URL:优先考虑 React Router。
  • 管理状态:优先使用 React State。
  • 页面存储:根据需要使用 Storage API。
  • 浏览器事件:可以配合 useEffect 注册和清理。

十、学习重点总结

知识点 优先级 需要掌握
window P0 全局对象的含义
location P0 URL 获取、页面跳转
localStorage P0 setItem、getItem
setTimeout P0 延迟执行
setInterval P0 定时执行与清理
history P1 pushState、back
sessionStorage P1 临时会话存储
navigator P1 常用环境 API
screen P2 了解即可

最后总结

BOM 主要解决 JavaScript 如何访问浏览器功能的问题。

最值得记住的是:

javascript 复制代码
// 当前地址
window.location.href;

// 页面跳转
window.location.assign("/login");

// 浏览器存储
localStorage.setItem("key", "value");

// 延迟执行
setTimeout(() => {}, 1000);

// 定时执行
setInterval(() => {}, 5000);

// 获取窗口宽度
window.innerWidth;

对于 React + TypeScript 业务开发,重点掌握 window、location、localStorage、定时器和浏览器事件即可。

至于其他 BOM API,实际工作中遇到了再查文档,不需要一次性全部学习。

相关推荐
Penge66644 分钟前
Vite vs Webpack:前端构建工具原理与实战
前端
Penge6661 小时前
前端包管理工具:npm、Yarn、pnpm 快速入门
前端
Penge6661 小时前
Sass 了解
前端
Penge6661 小时前
Webpack 打包从 0 到 1 实战笔记
前端
Penge6662 小时前
Webpack 入门:是什么、为什么需要、怎么使用?
前端
Penge6662 小时前
Ant Design(Antd)了解
前端
wangruofeng2 小时前
同一行代码,百种渲染:Asuka、React Native 与 Flutter 跨平台渲染机制深度拆解
前端·github
Penge6663 小时前
JavaScript 核心语法速通
前端
wangruofeng4 小时前
写给开发者的〈重构 UI〉实战笔记:从改好一个设置页开始,告别“程序员审美”
前端·aigc