一、history 对象:浏览历史管理
history
对象用于操作浏览器的会话历史记录,可实现前进、后退等导航功能,是单页应用(SPA)的核心技术之一。
1.1 基础历史操作
history
对象提供了控制历史记录的基础方法:
方法 | 说明 | 示例 |
---|---|---|
back() | 后退到上一页(等价于浏览器的后退按钮) | history.back() |
forward() | 前进到下一页(等价于浏览器的前进按钮) | history.forward() |
go(n) | 跳转到历史记录中第 n 个页面(n 为整数) | history.go(-2) (后退2页) |
length | 历史记录的长度(只读) | console.log(history.length) |
代码示例:基础历史导航
<button id="btnBack">后退</button>
<button id="btnForward">前进</button>
<button id="btnGoBack2">后退2页</button>
<button id="btnShowLength">显示历史长度</button>
<script>
document.getElementById("btnBack").addEventListener("click", () => {
history.back();
});
document.getElementById("btnForward").addEventListener("click", () => {
history.forward();
});
document.getElementById("btnGoBack2").addEventListener("click", () => {
// 若历史记录不足2条,无效果
history.go(-2);
});
document.getElementById("btnShowLength").addEventListener("click", () => {
alert(`历史记录长度:${history.length}`);
});
</script>
1.2 HTML5 扩展:pushState 与 replaceState
HTML5 新增了 pushState
和 replaceState
方法,允许在不刷新页面的情况下修改 URL 并添加历史记录,是单页应用实现无刷新导航的核心。
1.2.1 方法语法
history.pushState(state, title, url);
history.replaceState(state, title, url);
state
:状态对象(会被存储在历史记录中,可通过popstate
事件获取)title
:页面标题(多数浏览器忽略此参数,建议传空字符串)url
:新 URL(必须与当前页面同源,否则报错)
区别:
pushState
:添加新的历史记录条目replaceState
:替换当前历史记录条目(不增加历史长度)
代码示例:无刷新修改 URL
<div id="content">首页内容</div>
<button id="btnPage1">跳转到页面1</button>
<button id="btnPage2">跳转到页面2</button>
<script>
const content = document.getElementById("content");
// 跳转到页面1(pushState)
document.getElementById("btnPage1").addEventListener("click", () => {
// 添加新历史记录,URL 改为 /page1
history.pushState(
{ page: 1, title: "页面1" }, // state对象
"", // title(忽略)
"/page1" // URL
);
// 更新页面内容(模拟页面切换)
content.textContent = "页面1内容";
});
// 跳转到页面2(replaceState)
document.getElementById("btnPage2").addEventListener("click", () => {
// 替换当前历史记录,URL 改为 /page2
history.replaceState(
{ page: 2, title: "页面2" },
"",
"/page2"
);
content.textContent = "页面2内容";
});
// 监听历史记录变化(用户点击前进/后退按钮时触发)
window.addEventListener("popstate", (event) => {
console.log("历史记录变化,当前state:", event.state);
// 根据 state 更新页面内容
if (event.state) {
content.textContent = `${event.state.title}内容`;
} else {
// 若 state 为 null,显示首页
content.textContent = "首页内容";
}
});
</script>
1.2.2 popstate 事件
popstate
事件在用户点击前进/后退按钮或调用 history.back()
/forward()
/go()
时触发,事件对象的 state
属性包含对应历史记录的 state
对象。
注意:调用 pushState
或 replaceState
不会触发 popstate
事件,仅当用户主动改变历史记录时触发。
二、BOM 弹窗与窗口控制
BOM 提供了多种弹窗方式和窗口控制方法,用于与用户交互或管理浏览器窗口(注意:现代浏览器对弹窗有严格限制,避免滥用)。
2.1 原生弹窗:alert、confirm、prompt
浏览器内置了三种简单弹窗,用于基础交互:
2.1.1 alert():提示框
语法:window.alert(message)
alert("这是一个提示框");
// 示例:操作成功提示
function saveData() {
// 模拟保存数据
const isSuccess = true;
if (isSuccess) {
alert("数据保存成功!");
}
}
特点:
- 只有一个“确定”按钮
- 会阻塞代码执行(直到用户点击确定)
- 样式由浏览器控制,无法自定义
2.1.2 confirm():确认框
语法:const result = window.confirm(message)
const agree = confirm("是否删除这条数据?");
if (agree) {
console.log("用户点击了确定,执行删除");
} else {
console.log("用户点击了取消,取消删除");
}
特点:
- 有“确定”和“取消”两个按钮
- 返回布尔值(确定为 true,取消为 false)
- 同样阻塞代码执行
2.1.3 prompt():输入框
语法:const input = window.prompt(message, defaultValue)
const username = prompt("请输入用户名:", "admin");
if (username !== null) { // 用户点击确定(取消返回 null)
console.log("用户输入:", username);
} else {
console.log("用户取消输入");
}
特点:
- 包含输入框、“确定”和“取消”按钮
- 第二个参数为输入框默认值(可选)
- 返回用户输入的字符串(取消返回 null)
2.2 窗口控制:open() 与 close()
window.open()
用于打开新窗口,window.close()
用于关闭窗口(通常只能关闭由脚本打开的窗口)。
2.2.1 open() 方法详解
const newWindow = window.open(url, name, features);
url
:新窗口的 URL(为空则打开空白页)name
:窗口名称(或目标framename,如 “_blank” 表示新窗口)features
:窗口特性字符串(逗号分隔,如width=500,height=300
)
常用 features 参数:
参数 | 说明 | 示例值 |
---|---|---|
width | 窗口宽度(像素) | width=800 |
height | 窗口高度(像素) | height=600 |
left | 窗口左上角x坐标(像素) | left=100 |
top | 窗口左上角y坐标(像素) | top=100 |
toolbar | 是否显示工具栏(yes/no) | toolbar=no |
menubar | 是否显示菜单栏 | menubar=no |
scrollbars | 是否显示滚动条 | scrollbars=yes |
resizable | 窗口是否可调整大小 | resizable=yes |
status | 是否显示状态栏 | status=no |
代码示例:打开自定义窗口
function openCustomWindow() {
const url = "https://example.com";
const name = "exampleWindow";
// 窗口特性:宽800,高600,距离屏幕左、上各100px,无工具栏和菜单栏
const features = "width=800,height=600,left=100,top=100,toolbar=no,menubar=no,scrollbars=yes,resizable=yes";
const newWin = window.open(url, name, features);
// 若浏览器拦截弹窗
if (!newWin) {
alert("弹窗被浏览器拦截,请允许弹窗后重试");
} else {
// 2秒后聚焦新窗口
setTimeout(() => {
newWin.focus();
}, 2000);
}
}
2.2.2 close() 方法
// 关闭当前窗口(通常被浏览器禁止,除非窗口由脚本打开)
window.close();
// 关闭由脚本打开的窗口
const newWin = window.open("https://example.com");
setTimeout(() => {
newWin.close(); // 有效
}, 5000);
注意:现代浏览器通常禁止脚本关闭非脚本打开的窗口(如用户手动打开的标签页)。
三、定时器:setTimeout 与 setInterval
定时器是 BOM 提供的核心功能,用于延迟执行或重复执行代码,广泛应用于动画、轮询等场景。
3.1 setTimeout:延迟执行一次
setTimeout
用于在指定毫秒数后执行一次函数。
3.1.2 基本语法
语法:const timerId = setTimeout(callback, delay, arg1, arg2, ...)
const timerId = setTimeout(() => {
console.log("2秒后执行");
}, 2000);
// 带参数的回调
setTimeout((name, age) => {
console.log(`Hello, ${name}!你${age}岁了`);
}, 1000, "BOM", 5);
// 输出:"Hello, BOM!你5岁了"
callback
:延迟后执行的函数delay
:延迟时间(毫秒,默认0)- 后续参数:传递给回调函数的参数
- 返回值:定时器 ID(用于清除定时器)
3.1.2 清除定时器:clearTimeout
// 启动定时器
const timerId = setTimeout(() => {
console.log("这个不会执行");
}, 1000);
// 清除定时器(在执行前)
clearTimeout(timerId);
console.log("定时器已清除");
应用场景:按钮防重复点击
<button id="submitBtn">提交</button>
<script>
const btn = document.getElementById("submitBtn");
let isClicked = false;
btn.addEventListener("click", () => {
if (isClicked) return;
isClicked = true;
btn.disabled = true;
btn.textContent = "提交中...";
// 模拟接口请求
setTimeout(() => {
console.log("提交成功");
btn.textContent = "提交";
btn.disabled = false;
isClicked = false;
}, 2000);
});
</script>
3.2 setInterval:重复执行
setInterval
用于每隔指定毫秒数重复执行函数。
3.2.1 基本语法
语法:const timerId = setInterval(callback, interval, arg1, ...)
let count = 0;
const timerId = setInterval(() => {
count++;
console.log(`已执行${count}次`);
// 执行5次后停止
if (count >= 5) {
clearInterval(timerId);
console.log("定时器已停止");
}
}, 1000);
- 与
setTimeout
语法类似,但会重复执行 - 同样返回定时器 ID,用于清除
3.2.2 清除定时器:clearInterval
let seconds = 0;
const timerId = setInterval(() => {
seconds++;
console.log(`已过去${seconds}秒`);
}, 1000);
// 5秒后停止计时器
setTimeout(() => {
clearInterval(timerId);
console.log("计时器已停止");
}, 5000);
应用场景:倒计时
<div id="countdown">10</div>
<script>
const countdownEl = document.getElementById("countdown");
let remaining = 10;
const timerId = setInterval(() => {
remaining--;
countdownEl.textContent = remaining;
if (remaining <= 0) {
clearInterval(timerId);
countdownEl.textContent = "时间到!";
}
}, 1000);
</script>
3.3 定时器的this指向问题
定时器回调函数中的 this
默认指向 window
(非严格模式)或 undefined
(严格模式),需注意绑定正确的上下文。
解决方案:
- 使用箭头函数(继承外部
this
) - 使用
bind()
绑定this
const obj = {
name: "BOM",
// 方法1:箭头函数
startTimer1() {
setTimeout(() => {
console.log(this.name); // 输出:"BOM"(this指向obj)
}, 1000);
},
// 方法2:bind绑定
startTimer2() {
setTimeout(function() {
console.log(this.name); // 输出:"BOM"
}.bind(this), 1000);
}
};
obj.startTimer1();
obj.startTimer2();
四、本地存储:localStorage 与 sessionStorage
Web Storage API(localStorage
和 sessionStorage
)是 BOM 提供的本地数据存储方案,用于在浏览器中存储键值对数据(替代传统的 cookie)。
4.1 两者的区别
特性 | localStorage | sessionStorage |
---|---|---|
生命周期 | 永久存储(除非手动删除或清除浏览器数据) | 仅在当前会话有效(关闭标签页后删除) |
作用域 | 同源下所有标签页/窗口共享 | 仅当前标签页/窗口(即使同源) |
存储容量 | 约 5-10MB | 约 5-10MB |
数据共享 | 同源页面可共享 | 不可跨标签页共享 |
4.2 核心方法与属性
两者的 API 完全一致:
方法/属性 | 说明 |
---|---|
setItem(key, value) | 存储数据(value 必须为字符串) |
getItem(key) | 获取数据(返回字符串,不存在则为 null) |
removeItem(key) | 删除指定键的数据 |
clear() | 清空所有数据 |
length | 存储的键值对数量 |
key(index) | 获取指定索引的键名 |
代码示例:localStorage 基本操作
// 存储数据
localStorage.setItem("username", "BOM");
localStorage.setItem("age", "10"); // 数字也会被转为字符串
// 获取数据
const username = localStorage.getItem("username");
console.log(username); // 输出:"BOM"
// 存储对象(需先转为字符串)
const user = { name: "BOM", skills: ["JS", "HTML"] };
localStorage.setItem("user", JSON.stringify(user));
// 获取对象(需解析)
const storedUser = JSON.parse(localStorage.getItem("user"));
console.log(storedUser.skills); // 输出:["JS", "HTML"]
// 删除数据
localStorage.removeItem("age");
// 清空所有数据(谨慎使用)
// localStorage.clear();
// 遍历所有数据
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
console.log(`${key}: ${value}`);
}
注意:
- 仅支持字符串存储,复杂类型需用
JSON.stringify
转换 - 不要存储敏感信息(明文存储,易被获取)
- 存储大量数据可能影响页面性能
4.3 storage 事件
当 localStorage
的数据发生变化时(新增、修改、删除),同源下的其他页面会触发 storage
事件(当前页面不会触发)。
代码示例:监听存储变化
// 在页面 A中监听
window.addEventListener("storage", (event) => {
console.log("存储发生变化:");
console.log("旧值:", event.oldValue);
console.log("新值:", event.newValue);
console.log("键名:", event.key);
console.log("源地址:", event.url); // 触发变化的页面URL
});
// 在页面 B中修改数据(会触发页面A的storage事件)
localStorage.setItem("username", "NewBOM");
五、BOM 事件:监听浏览器行为
BOM 提供了一系列事件用于监听浏览器窗口的状态变化,如窗口大小改变、滚动、关闭等。
5.1 resize:窗口大小变化
resize
事件在窗口大小改变时触发(包括浏览器窗口缩放、最大化/最小化)。
代码示例:响应窗口大小变化
<div id="sizeInfo">窗口大小:宽 × 高</div>
<script>
const sizeInfo = document.getElementById("sizeInfo");
// 更新尺寸信息的函数
function updateSize() {
const width = window.innerWidth;
const height = window.innerHeight;
sizeInfo.textContent = `窗口大小:${width} × ${height}`;
}
// 初始调用
updateSize();
// 监听窗口大小变化
window.addEventListener("resize", updateSize);
</script>
优化:频繁触发的事件(如 resize)可使用节流(throttle)优化性能:
// 节流函数(限制事件触发频率)
function throttle(func, delay = 100) {
let lastTime = 0;
return (...args) => {
const now = Date.now();
if (now - lastTime >= delay) {
func.apply(this, args);
lastTime = now;
}
};
}
// 使用节流后的函数监听resize
window.addEventListener("resize", throttle(updateSize, 200)); // 每200ms最多执行一次
5.2 scroll:页面滚动
scroll
事件在页面或元素滚动时触发,常用于实现滚动加载、回到顶部按钮等功能。
代码示例:滚动时显示回到顶部按钮
<button id="backToTop" style="position: fixed; bottom: 20px; right: 20px; display: none;">回到顶部</button>
<script>
const backToTopBtn = document.getElementById("backToTop");
// 监听页面滚动
window.addEventListener("scroll", () => {
// 获取滚动距离(文档顶部到可视区顶部)
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
// 滚动超过300px时显示按钮
if (scrollTop > 300) {
backToTopBtn.style.display = "block";
} else {
backToTopBtn.style.display = "none";
}
});
// 回到顶部功能
backToTopBtn.addEventListener("click", () => {
// 平滑滚动到顶部
window.scrollTo({
top: 0,
behavior: "smooth"
});
});
</script>
六、BOM 兼容性与最佳实践
6.1 兼容性处理
虽然主流浏览器对 BOM 核心 API 支持良好,但部分旧浏览器(如 IE8 及以下)存在差异:
addEventListener
在 IE8 中不支持,需使用attachEvent
localStorage
在 IE7 及以下不支持geolocation
在 IE9 及以下不支持history.pushState
在 IE9 及以下不支持
兼容写法示例(事件监听):
function addEvent(element, event, handler) {
if (element.addEventListener) {
element.addEventListener(event, handler);
} else if (element.attachEvent) { // IE8及以下
element.attachEvent("on" + event, handler);
} else {
element["on" + event] = handler;
}
}
// 使用
addEvent(window, "resize", () => {
console.log("窗口大小变化");
});
6.2 最佳实践
- 避免滥用弹窗:alert/confirm/prompt 体验差,推荐使用自定义弹窗组件
- 及时清除定时器:组件卸载或页面关闭前清除定时器,避免内存泄漏
- 慎用
eval
:window.eval
执行字符串代码,存在安全风险和性能问题 - 本地存储安全:不存储敏感信息(如密码),敏感数据需加密
- 特性检测优先:用
typeof
或in
检测 API 是否存在,而非浏览器检测 - 防抖节流:对 resize、scroll 等高频事件使用防抖或节流优化性能
- 历史记录管理:SPA 中使用
pushState
时,确保服务器能正确处理新 URL 的请求
掌握 BOM 是前端开发的基础,合理使用 BOM 可以显著提升用户体验,但需注意兼容性和性能问题,遵循最佳实践。
BOM 与 DOM 共同构成了前端开发的核心基础,深入理解两者的协同工作机制,将为构建复杂交互的 web 应用奠定坚实基础。