Skip to content

BOM 基础

BOM(Browser Object Model)是浏览器提供的对象模型,用于与浏览器窗口进行交互。BOM 的核心是 window 对象。

🎯 window 对象#

全局作用域#

// window 是全局对象
var globalVar = '全局变量'
console.log(window.globalVar) // '全局变量'
// 全局函数也是 window 的属性
function globalFunc() {
return 'hello'
}
console.log(window.globalFunc()) // 'hello'
// 🔶 let/const 不会成为 window 属性
let letVar = 'let 变量'
console.log(window.letVar) // undefined

窗口尺寸#

// 视口尺寸(不含滚动条)
window.innerWidth // 视口宽度
window.innerHeight // 视口高度
// 窗口尺寸(含工具栏等)
window.outerWidth // 窗口总宽度
window.outerHeight // 窗口总高度
// 文档尺寸
document.documentElement.clientWidth // 文档宽度
document.documentElement.clientHeight // 文档高度
// 屏幕尺寸
screen.width // 屏幕宽度
screen.height // 屏幕高度
screen.availWidth // 可用宽度(排除任务栏)
screen.availHeight // 可用高度
// 滚动位置
window.scrollX // 水平滚动距离
window.scrollY // 垂直滚动距离
// 或
window.pageXOffset
window.pageYOffset

滚动控制#

// 滚动到指定位置
window.scrollTo(0, 100) // 滚动到 y=100
window.scrollTo({
top: 100,
left: 0,
behavior: 'smooth', // 平滑滚动
})
// 相对滚动
window.scrollBy(0, 100) // 向下滚动 100px
window.scrollBy({
top: 100,
behavior: 'smooth',
})
// 滚动元素到视图
element.scrollIntoView()
element.scrollIntoView({ behavior: 'smooth', block: 'center' })

弹窗方法#

// 警告框
window.alert('提示信息')
// 确认框
const confirmed = window.confirm('确定删除吗?')
if (confirmed) {
// 用户点击了确定
}
// 输入框
const name = window.prompt('请输入姓名', '默认值')
if (name !== null) {
console.log('输入:', name)
}
// 🔶 这些方法会阻塞 JavaScript 执行
// 现代开发中尽量避免使用,改用自定义弹窗

打开新窗口#

// 打开新窗口/标签页
const newWindow = window.open('https://example.com', '_blank')
// 指定窗口大小和位置
window.open(
'https://example.com',
'popup',
'width=400,height=300,left=100,top=100'
)
// 关闭窗口
newWindow.close()
// 检查窗口是否关闭
if (newWindow.closed) {
console.log('窗口已关闭')
}
// 🔶 弹窗可能被浏览器拦截
// 最好由用户操作触发(如点击事件)

location 对象#

URL 信息#

// 假设当前 URL: https://example.com:8080/path/page.html?id=123#section
location.href // 完整 URL
location.protocol // 'https:'
location.host // 'example.com:8080'
location.hostname // 'example.com'
location.port // '8080'
location.pathname // '/path/page.html'
location.search // '?id=123'
location.hash // '#section'
location.origin // 'https://example.com:8080'

页面跳转#

// 方式1:设置 href(会留下历史记录)
location.href = 'https://example.com'
// 方式2:使用 assign(等同于设置 href)
location.assign('https://example.com')
// 方式3:使用 replace(不留历史记录)
location.replace('https://example.com')
// 刷新页面
location.reload() // 普通刷新
location.reload(true) // 强制刷新(从服务器)
// 只改变 hash(不刷新页面)
location.hash = '#new-section'

URL 参数解析#

// 获取查询参数
const params = new URLSearchParams(location.search)
// 获取单个参数
params.get('id') // '123'
params.get('name') // null(不存在)
// 检查参数是否存在
params.has('id') // true
// 获取所有参数
for (const [key, value] of params) {
console.log(`${key}: ${value}`)
}
// 获取同名参数的所���值
params.getAll('tags') // ['a', 'b'](如果有 ?tags=a&tags=b)
// 转为对象
const paramsObj = Object.fromEntries(params)
// 修改参数
params.set('id', '456')
params.append('new', 'value')
params.delete('id')
// 转回字符串
params.toString() // 'new=value'

构建 URL#

// 使用 URL 对象
const url = new URL('https://example.com/path')
url.searchParams.set('id', '123')
url.searchParams.set('name', '张三')
url.hash = 'section'
console.log(url.href)
// 'https://example.com/path?id=123&name=%E5%BC%A0%E4%B8%89#section'
// 相对 URL
const relative = new URL('/api/users', 'https://example.com')
console.log(relative.href) // 'https://example.com/api/users'

history 对象#

历史导航#

// 历史记录长度
history.length
// 后退
history.back() // 等同于点击后退按钮
// 前进
history.forward() // 等同于点击前进按钮
// 跳转到指定历史
history.go(-1) // 后退一页
history.go(1) // 前进一页
history.go(-2) // 后退两页
history.go(0) // 刷新当前页

History API(SPA 路由)#

// 添加历史记录(不刷新页面)
history.pushState(
{ page: 1 }, // state 对象
'', // title(大多数浏览器忽略)
'/page/1' // URL
)
// 替换当前历史记录
history.replaceState({ page: 2 }, '', '/page/2')
// 获取当前状态
console.log(history.state) // { page: 2 }
// 监听前进/后退
window.addEventListener('popstate', (event) => {
console.log('导航到:', location.pathname)
console.log('状态:', event.state)
})
// 🔶 注意:pushState 不会触发 popstate
// 只有浏览器的前进/后退按钮会触发

简单路由实现#

class Router {
constructor() {
this.routes = {}
window.addEventListener('popstate', () => this.handleRoute())
}
on(path, handler) {
this.routes[path] = handler
}
navigate(path, state = {}) {
history.pushState(state, '', path)
this.handleRoute()
}
handleRoute() {
const path = location.pathname
const handler = this.routes[path]
if (handler) {
handler(history.state)
}
}
}
// 使用
const router = new Router()
router.on('/', () => console.log('首页'))
router.on('/about', () => console.log('关于页'))
router.on('/user', (state) => console.log('用户��', state))
router.navigate('/user', { id: 123 })

浏览器信息#

// 用户代理字符串
navigator.userAgent
// 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...'
// 浏览器名称和版本
navigator.appName // 'Netscape'(历史遗留)
navigator.appVersion // 版本信息
// 平台
navigator.platform // 'Win32', 'MacIntel', 'Linux x86_64'
// 语言
navigator.language // 'zh-CN'
navigator.languages // ['zh-CN', 'zh', 'en']
// 是否在线
navigator.onLine // true/false
// CPU 核心数
navigator.hardwareConcurrency // 8
// 设备内存(GB)
navigator.deviceMemory // 8

特性检测#

// 🔶 不推荐使用 userAgent 检测浏览器
// ✅ 推荐使用特性检测
// 检测是否支持某个 API
if ('geolocation' in navigator) {
// 支持地理定位
}
if ('serviceWorker' in navigator) {
// 支持 Service Worker
}
if ('share' in navigator) {
// 支持 Web Share API
}
if ('clipboard' in navigator) {
// 支持剪贴板 API
}
// 检测移动设备(使用媒体查询)
const isMobile = window.matchMedia('(max-width: 768px)').matches
// 或检测触摸支持
const hasTouch = 'ontouchstart' in window

常用 API#

// 地理定位
navigator.geolocation.getCurrentPosition(
(position) => {
console.log('纬度:', position.coords.latitude)
console.log('经度:', position.coords.longitude)
},
(error) => {
console.error('定位失败:', error.message)
}
)
// 剪贴板
await navigator.clipboard.writeText('复制的文本')
const text = await navigator.clipboard.readText()
// 分享
if (navigator.share) {
await navigator.share({
title: '标题',
text: '描述',
url: 'https://example.com',
})
}
// 震动(移动设备)
navigator.vibrate(200) // 震动 200ms
navigator.vibrate([100, 50, 100]) // 震动-暂停-震动

screen 对象#

// 屏幕尺寸
screen.width // 屏幕宽度
screen.height // 屏幕高度
// 可用尺寸(排除任务栏等)
screen.availWidth // 可用宽度
screen.availHeight // 可用��度
// 颜色深度
screen.colorDepth // 24(位)
screen.pixelDepth // 24
// 屏幕方向(移动设备)
screen.orientation.type // 'portrait-primary', 'landscape-primary'
screen.orientation.angle // 0, 90, 180, 270
// 监听方向变化
screen.orientation.addEventListener('change', () => {
console.log('方向:', screen.orientation.type)
})

实际应用#

响应式检测#

// 使用媒体查询
const mediaQuery = window.matchMedia('(max-width: 768px)')
function handleMediaChange(e) {
if (e.matches) {
console.log('移动端视图')
} else {
console.log('桌面端视图')
}
}
mediaQuery.addEventListener('change', handleMediaChange)
handleMediaChange(mediaQuery) // 初始检查

全屏 API#

// 进入全屏
async function enterFullscreen(element = document.documentElement) {
try {
await element.requestFullscreen()
} catch (error) {
console.error('全屏失败:', error)
}
}
// 退出全屏
function exitFullscreen() {
if (document.fullscreenElement) {
document.exitFullscreen()
}
}
// 检查是否全屏
document.fullscreenElement // 当前全屏元素或 null
// 监听全屏变化
document.addEventListener('fullscreenchange', () => {
console.log('全屏状态:', !!document.fullscreenElement)
})

页面可见性#

// 检测页面是否可见
document.visibilityState // 'visible', 'hidden', 'prerender'
document.hidden // true/false
// 监听可见性变化
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
console.log('页面隐藏')
// 暂停视频、动画等
} else {
console.log('页面可见')
// 恢复操作
}
})

离开页面提示#

// 提示用户有未保存的更改
let hasUnsavedChanges = false
window.addEventListener('beforeunload', (event) => {
if (hasUnsavedChanges) {
event.preventDefault()
event.returnValue = '' // 显示确认对话框
}
})
// 标记有未保存更改
input.addEventListener('change', () => {
hasUnsavedChanges = true
})
// 保存后清除标记
function save() {
// 保存逻辑
hasUnsavedChanges = false
}

总结#

对象用途
window全局对象,窗口控制
locationURL 信息和页面跳转
history历史记录管理
navigator浏览器/设备信息
screen屏幕信息
常用操作方法
页面跳转location.href = url
刷新页面location.reload()
后退history.back()
前进history.forward()
添加历史history.pushState()
获取参数new URLSearchParams(location.search)

核心要点