返回首页

说说你对BOM的理解?

问题解析

BOM(Browser Object Model,浏览器对象模型)是 JavaScript 中与浏览器进行交互的一套 API。它提供了独立于页面内容的、与浏览器窗口进行交互的对象,使开发者能够控制浏览器的行为、获取浏览器信息、操作浏览器窗口等。

核心概念

BOM 与 DOM 的区别

特性 BOM DOM
全称 Browser Object Model Document Object Model
操作对象 浏览器窗口 HTML/XML 文档
核心对象 window document
标准化 无统一标准(各浏览器实现略有差异) W3C 标准
内容 窗口控制、导航、屏幕信息等 文档结构、元素操作

关系:BOM 包含 DOM,DOM 是 BOM 的一部分。

window (BOM 核心)
├── document (DOM)
├── location
├── navigator
├── screen
├── history
└── frames

详细解答

一、window 对象

window 是 BOM 的核心对象,它代表浏览器的一个实例,也是 JavaScript 的全局对象。

1. 全局作用域

// 在全局作用域声明的变量和函数都会成为 window 的属性和方法
var name = 'JavaScript';
function sayName() {
    console.log(this.name);
}

console.log(window.name);    // "JavaScript"
window.sayName();            // "JavaScript"

// 注意:let 和 const 声明的变量不会成为 window 的属性
let age = 25;
const PI = 3.14;
console.log(window.age);     // undefined
console.log(window.PI);      // undefined

2. 窗口控制方法

// 移动窗口(部分浏览器限制)
window.moveBy(100, 100);     // 相对当前位置移动(水平,垂直)
window.moveTo(100, 100);     // 移动到指定位置

// 调整窗口大小(部分浏览器限制)
window.resizeBy(200, 200);   // 相对当前大小调整
window.resizeTo(800, 600);   // 调整到指定大小

// 滚动窗口
window.scrollTo(0, 500);     // 滚动到指定位置
window.scrollBy(0, 100);     // 相对当前位置滚动

// 平滑滚动
window.scrollTo({
    top: 500,
    behavior: 'smooth'
});

3. 窗口打开与关闭

// 打开新窗口
const newWindow = window.open(
    'https://www.example.com',  // URL
    'myWindow',                  // 窗口名称(用于 target 属性)
    'width=800,height=600,left=100,top=100,resizable=yes'
);

// 窗口特性参数
// width/height: 窗口宽高
// left/top: 窗口位置
// resizable: 是否可调整大小
// scrollbars: 是否显示滚动条
// status: 是否显示状态栏

// 关闭窗口
newWindow.close();

// 检查窗口是否关闭
console.log(newWindow.closed);  // true 或 false

// 获取打开当前窗口的窗口引用
console.log(window.opener);     // 如果有父窗口则返回引用

4. 定时器

// setTimeout - 延迟执行(一次)
const timeoutId = setTimeout(() => {
    console.log('3秒后执行');
}, 3000);

// 取消定时器
clearTimeout(timeoutId);

// setInterval - 间隔执行(重复)
const intervalId = setInterval(() => {
    console.log('每2秒执行一次');
}, 2000);

// 取消定时器
clearInterval(intervalId);

// requestAnimationFrame - 浏览器重绘前执行(动画推荐)
function animate() {
    // 执行动画
    console.log('动画帧');
    requestAnimationFrame(animate);
}
const rafId = requestAnimationFrame(animate);
cancelAnimationFrame(rafId);

5. 系统对话框

// 警告框
window.alert('这是一个警告');

// 确认框
const isConfirmed = window.confirm('确定要删除吗?');
console.log(isConfirmed);  // true(确定)或 false(取消)

// 输入框
const userInput = window.prompt('请输入您的名字', '默认值');
console.log(userInput);    // 用户输入的内容,取消则返回 null

// 打印对话框
window.print();

// 查找对话框
window.find('search text');

二、location 对象

location 对象提供了当前窗口中加载文档的信息,以及导航功能。

// 完整 URL:http://www.example.com:8080/path/page.html?q=1#section

console.log(location.href);      // 完整 URL
console.log(location.protocol);  // "http:"
console.log(location.host);      // "www.example.com:8080"
console.log(location.hostname);  // "www.example.com"
console.log(location.port);      // "8080"
console.log(location.pathname);  // "/path/page.html"
console.log(location.search);    // "?q=1"
console.log(location.hash);      // "#section"
console.log(location.origin);    // "http://www.example.com:8080"

导航方法

// 跳转到新页面(会在浏览器历史中添加记录)
location.href = 'https://www.example.com';
// 或
location.assign('https://www.example.com');

// 替换当前页面(不会在历史中添加记录)
location.replace('https://www.example.com');

// 重新加载页面
location.reload();           // 从缓存加载
location.reload(true);       // 从服务器重新加载

// 解析 URL 查询参数
function getQueryParam(name) {
    const params = new URLSearchParams(location.search);
    return params.get(name);
}
console.log(getQueryParam('q'));

三、navigator 对象

navigator 对象包含了浏览器的信息,常用于检测浏览器类型、版本,以及获取用户设备信息。

// 浏览器信息
console.log(navigator.userAgent);      // 用户代理字符串
console.log(navigator.appName);        // 浏览器名称
console.log(navigator.appVersion);     // 浏览器版本
console.log(navigator.platform);       // 操作系统平台

// 语言
console.log(navigator.language);       // 首选语言
console.log(navigator.languages);      // 语言列表

// 网络状态
console.log(navigator.onLine);         // 是否在线

// 监听网络状态变化
window.addEventListener('online', () => {
    console.log('网络已连接');
});
window.addEventListener('offline', () => {
    console.log('网络已断开');
});

// 地理位置
navigator.geolocation.getCurrentPosition(
    position => {
        console.log('纬度:', position.coords.latitude);
        console.log('经度:', position.coords.longitude);
    },
    error => {
        console.error('获取位置失败:', error);
    }
);

// 剪贴板 API
navigator.clipboard.writeText('复制的内容')
    .then(() => console.log('已复制到剪贴板'));

navigator.clipboard.readText()
    .then(text => console.log('剪贴板内容:', text));

// 媒体设备(摄像头、麦克风)
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
    .then(stream => {
        // 使用媒体流
    });

// 电池状态(部分浏览器支持)
navigator.getBattery().then(battery => {
    console.log('电量:', battery.level * 100 + '%');
    console.log('是否充电:', battery.charging);
});

// 分享功能(Web Share API)
navigator.share({
    title: '分享标题',
    text: '分享内容',
    url: 'https://www.example.com'
});

四、screen 对象

screen 对象包含了客户端显示器的信息。

// 屏幕尺寸
console.log(screen.width);           // 屏幕宽度
console.log(screen.height);          // 屏幕高度
console.log(screen.availWidth);      // 可用宽度(除去任务栏等)
console.log(screen.availHeight);     // 可用高度

// 颜色深度
console.log(screen.colorDepth);      // 颜色位数(如 24)
console.log(screen.pixelDepth);      // 像素深度

// 屏幕方向
console.log(screen.orientation);
// 监听方向变化
screen.orientation.addEventListener('change', () => {
    console.log('屏幕方向改变:', screen.orientation.type);
});

五、history 对象

history 对象用于操作浏览器的会话历史(即访问过的页面)。

// 历史记录数量
console.log(history.length);

// 前进/后退
history.back();       // 后退一页(等同于点击后退按钮)
history.forward();    // 前进一页
history.go(-1);       // 后退一页
history.go(1);        // 前进一页
history.go(0);        // 刷新当前页

// HTML5 History API
// 添加历史记录(不刷新页面)
history.pushState(
    { page: 1 },           // state 对象
    'Page Title',          // 标题(大部分浏览器忽略)
    '/new-path'            // URL(必须同源)
);

// 替换当前历史记录
history.replaceState(
    { page: 2 },
    'New Title',
    '/another-path'
);

// 监听历史记录变化
window.addEventListener('popstate', event => {
    console.log('历史记录变化:', event.state);
});

单页应用(SPA)路由实现

// 简单的路由系统
class Router {
    constructor() {
        this.routes = {};
        window.addEventListener('popstate', this.handlePopState.bind(this));
    }

    route(path, handler) {
        this.routes[path] = handler;
    }

    navigate(path) {
        history.pushState({ path }, '', path);
        this.handleRoute(path);
    }

    handlePopState(event) {
        const path = location.pathname;
        this.handleRoute(path);
    }

    handleRoute(path) {
        const handler = this.routes[path];
        if (handler) {
            handler();
        }
    }
}

// 使用
const router = new Router();
router.route('/', () => console.log('首页'));
router.route('/about', () => console.log('关于页面'));

// 导航
document.getElementById('about-link').addEventListener('click', () => {
    router.navigate('/about');
});

深入理解

浏览器检测

// 检测浏览器类型
function detectBrowser() {
    const ua = navigator.userAgent;

    if (ua.indexOf('Chrome') > -1 && ua.indexOf('Edg') === -1) {
        return 'Chrome';
    } else if (ua.indexOf('Safari') > -1 && ua.indexOf('Chrome') === -1) {
        return 'Safari';
    } else if (ua.indexOf('Firefox') > -1) {
        return 'Firefox';
    } else if (ua.indexOf('Edg') > -1) {
        return 'Edge';
    } else if (ua.indexOf('Opera') > -1 || ua.indexOf('OPR') > -1) {
        return 'Opera';
    }
    return 'Unknown';
}

// 检测移动设备
function isMobile() {
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}

// 检测特性支持(更推荐的方式)
if ('geolocation' in navigator) {
    // 支持地理位置
}

if ('serviceWorker' in navigator) {
    // 支持 Service Worker
}

跨窗口通信

// 同源窗口间通信
const popup = window.open('https://same-origin.com/page.html', 'popup');

// 发送消息
popup.postMessage('Hello from parent', 'https://same-origin.com');

// 接收消息
window.addEventListener('message', event => {
    // 验证来源
    if (event.origin !== 'https://same-origin.com') return;

    console.log('收到消息:', event.data);
    console.log('来源:', event.origin);
    console.log('源窗口:', event.source);
});

最佳实践

  1. 避免使用浏览器检测,优先使用特性检测

    // ❌ 不推荐
    if (navigator.userAgent.indexOf('Chrome') > -1) {
        // Chrome 特有功能
    }
    
    // ✅ 推荐
    if ('geolocation' in navigator) {
        // 使用地理位置
    }
    
  2. 使用 History API 实现 SPA 时,要处理服务器端配置

  3. 使用 window.open 时要注意弹窗拦截

    // 在用户交互回调中打开窗口,避免被拦截
    button.addEventListener('click', () => {
        window.open('https://example.com', '_blank');
    });
    
  4. 清理定时器,避免内存泄漏

    const timer = setTimeout(() => {}, 1000);
    // 组件卸载时清理
    clearTimeout(timer);
    
  5. 使用 URLSearchParams 处理查询参数

    const params = new URLSearchParams(location.search);
    const value = params.get('key');
    

面试要点

  1. 什么是 BOM?

    • Browser Object Model,浏览器对象模型
    • 提供与浏览器窗口交互的 API
    • 核心对象是 window
  2. BOM 和 DOM 的区别?

    • BOM 操作浏览器窗口,DOM 操作文档内容
    • BOM 没有统一标准,DOM 是 W3C 标准
    • BOM 包含 DOM(window.document)
  3. window 对象的常用方法?

    • 窗口控制:open、close、moveTo、resizeTo
    • 定时器:setTimeout、setInterval、requestAnimationFrame
    • 对话框:alert、confirm、prompt
    • 滚动:scrollTo、scrollBy
  4. location 和 history 对象的作用?

    • location:获取/设置 URL,页面导航
    • history:操作浏览器历史记录,实现前进/后退/SPA 路由
  5. navigator 对象可以获取哪些信息?

    • 浏览器类型和版本(userAgent)
    • 操作系统平台
    • 网络状态(onLine)
    • 地理位置
    • 各种现代 API(剪贴板、媒体设备、电池等)
  6. 如何实现单页应用(SPA)的路由?

    • 使用 history.pushState/replaceState 改变 URL 不刷新页面
    • 监听 popstate 事件处理前进/后退
    • 根据 URL 路径渲染对应组件