精读《JavaScript 高级程序设计 第4版》第12章 BOM
BOM 指的是浏览器对象模型。它不是像DOM那样的官方标准(W3C或WHATWG),但它被所有现代浏览器所实现,提供了一组用于与浏览器窗口本身进行交互的对象。
核心思想:BOM的核心是 window 对象。在浏览器中,window 对象扮演着双重角色:
- 它是ECMAScript中的全局对象。所有在全局作用域中声明的变量和函数都会成为
window对象的属性和方法。 - 它是浏览器窗口的JavaScript接口,提供了控制和访问浏览器窗口的能力。
12.1 Window对象
BOM的核心是window对象,表示浏览器的实例。window 对象扮演着双重角色,一个是ECMAScript中的Global对象,另一个就是浏览器窗口的JavaScript接口。这意味着网页中定义的所有对象、变量和函数都以window作为其Global对象,都可以访问其上定义的parseInt()等全局方法。
12.1.1 Global作用域
所有通过var声明的全局变量和函数都会成为window对象的属性:
var greeting = 'Hello World';
console.log(window.greeting); // 'Hello World'
现代注意点:使用 let 和 const 声明的全局变量不会成为 window 对象的属性。
let greetingLet = 'Hello';
const greetingConst = 'World';
console.log(window.greetingLet); // undefined
console.log(window.greetingConst); // undefined
12.1.2 窗口关系
top:始终指向最顶层(最外层)窗口,即浏览器标签页本身。parent:指向当前窗口的父窗口(如果当前窗口是<iframe>,则parent指向包含它的窗口)。self:始终指向当前的window对象(与window是同义词)。
现代应用:在单页应用和微前端架构中,处理iframe与主应用、或窗口与弹出窗口之间的通信时,这些属性至关重要。
12.1.3 窗口位置与像素比
screenLeft/screenTop:窗口相对于屏幕左侧和顶部的位置,返回值单位是CSS像素。screenX/screenY:同上,是标准属性。moveTo()/moveBy():移动窗口。都接收两个参数,moveTo()接收要移动到的新位置的绝对坐标;moveBy()则接收相对当前位置在两个方向上移动的像素数。(依浏览器而定,可能被禁用)devicePixelRatio:物理像素与CSS像素之间的转换比率。值为3时,12像素(CSS)的文字实际上会使用36像素的物理像素来显示。
12.1.4 窗口大小
innerWidth/innerHeight:视口(viewport) 的尺寸,即页面实际渲染区域的宽高(不包括浏览器UI、滚动条)。outerWidth/outerHeight:整个浏览器窗口的尺寸。resizeTo()/resizeBy():调整窗口大小,接收两个参数,resizeTo()接收新的宽高值,resizeBy()接收宽高各要缩放多少。(可能被部分浏览器禁用)
12.1.5 视口位置
pageXoffset/pageYoffset:返回文档相对于视口的滚动距离scrollX/scrollY:同上scroll()/scrollTo()/scrollBy():滚动页面,都接收两个参数表示相对视口距离的x和y坐标,scrollTo()表示要滚动到的位置,scrollBy()表示要滚动的距离。也都接收一个ScrollToOptions字典,通过behavior属性告诉浏览器是否平滑滚动:
window.scrollTo({
left: 100,
top: 100,
behavior: 'smooth'
})
12.1.6 导航与打开新窗口
window.open() 方法用于导航到一个新URL或打开一个新浏览器窗口。
用法:window.open(URL, target, features)
-
URL:要加载的地址。target:窗口目标,如_blank,_self,或一个窗口/框架名。features:一个逗号分隔的设置字符串,用于控制新窗口的UI元素(如工具栏、状态栏、大小等)。
const popup = window.open('https://www.example.com', 'exampleWindow', 'width=400,height=300');
新创建窗口的window对象都有一个属性opener,指向打开它的窗口,可以通过将指定窗口对象的opener属性值置null,实现新窗口的运行在独立进程中。(一旦切断就无法恢复)
- 现代安全限制与最佳实践:
-
- 弹出窗口阻止程序:现代浏览器默认会阻止非用户触发的
window.open()调用(例如在setTimeout或异步回调中直接调用)。必须由用户手势(如点击)发起。 - 慎用:由于糟糕的用户体验和潜在的滥用(广告、钓鱼),应尽量避免使用弹出窗口。如果需要打开新标签页,通常使用
target="_blank"的<a>标签是更好的选择。 - 通信:通过
window.open()返回的引用,可以实现父子窗口间的有限通信(如popup.close())。 popup.close()只能用于window.open()创建的窗口。
- 弹出窗口阻止程序:现代浏览器默认会阻止非用户触发的
12.1.7 定时器 (setTimeout & setInterval)
这是BOM中最常用、最稳定的API之一。
setTimeout(func, delay):在指定的毫秒数delay后,将任务func推入任务队列执行。返回一个ID,可用于取消。setInterval(func, delay):每隔指定的毫秒数delay,重复将任务func推入任务队列执行。返回一个ID,可用于取消。
现代注意点:
-
- 最小延迟:在非活动标签页中,现代浏览器会对定时器实施最小延迟(例如1秒)以节省资源和电池。
- 性能与替代方案:对于高频重复动画,
requestAnimationFrame是比setInterval更优的选择,因为它与浏览器的重绘周期同步,能保证动画的流畅性并节省电量。
// 使用 requestAnimationFrame 实现动画
function animate() {
// ... 更新动画状态 ...
element.style.left = `${newPosition}px`;
requestAnimationFrame(animate);
}
animate();
-
- 取消定时器:务必使用
clearTimeout(timeoutId)和clearInterval(intervalId)来清理不再需要的定时器,防止内存泄漏。
- 取消定时器:务必使用
12.1.8 系统对话框
浏览器调用系统对话框向用户显示消息,这些对话框与网页无关,不包含HTML,外观由操作系统或浏览器决定,无法使用CSS设置。且这些对话框都是同步的静态对话框,显示时代码会停止执行,消失后代码才恢复执行。
alert():警告框,接收一个要显示给用户的字符串,该字符串会显示在对话框中,对话框只有一个"OK"(确定)按钮。confirm():确认框,与alert()不同的点在于有两个按钮:"Cancel"(取消)和"OK"(确定)。该方法返回Boolean值表示用户点击ok还是Cancel。- prompt():提示框,提示用户输入消息,除了ok和Cancel按钮,还会显示一个文本框,让用户输入内容。该方法接收两个参数,要显示给用户的文本和文本框的默认值(可空串),返回文本中的值或者取消时返回null。
12.2 location对象
它提供了当前窗口加载文档的信息和导航功能。它既是 window 的属性,也是 document 的属性 (window.location === document.location)。
- 属性:包含了URL的各个部分。
-
location.href:完整的URL。(如"http://www.wrox.com:80/WileyCDA/?q=javascript#contents")location.protocol:协议(如 "http:")。location.host:服务器名和端口号,(如"http://www.wrox.com:80" )。location.hostname:服务器名,(如"www.wrox.com" )。location.port:端口号,(如"80" )。location.pathname:URL中的路径,(如"/WileyCDA/" )。location.search:?之后的查询字符串,(如 "?q=javascript")。location.hash:#之后的片段标识符,(如 "#contents")。
12.2.1 查询字符串
- location.search():返回从问号开始直到URL末尾的所有内容,需要自己编写函数进行二次处理。
- URLSearchParams():URLSearchParams提供了一组标准API方法,给该构造函数传入一个查询字符串,就可以创建一个实例。这个实例暴露了get()、set()和delete()等方法,可以对查询字符串执行相应操作。
let qs = "?q=javascript&num=10";
let searchParams = new URLSearchParams(qs);
alert(searchParams.toString()); //" q=javascript&num=10"
searchParams.has("num"); //true
searchParams.get("num"); //10
searchParams.set("page", "3");
alert(searchParams.toString()); //" q=javascript&num=10&page=3"
searchParams.delete("num");
alert(searchParams.toString()); //" q=javascrip&page=3"
12.2.2 操作地址
location.assign("https://..."):导航到新URL,在历史记录中生成一条记录。location.replace("https://..."):导航到新URL,替换当前历史记录,用户不能点击“返回”按钮回到前一个页面。location.reload():重新加载当前页面。传true会强制从服务器重新加载(绕过缓存)。- 修改location对象属性也会修改当前加载的页面,使得页面重新加载新URL(例如:
location.hostname="www.baidu.com";)
现代应用:
- 单页应用:在React, Vue等SPA框架中,我们通常使用客户端路由库(如React Router, Vue Router)。这些库的核心就是通过监听
location.hash(Hash模式)或使用History API(History模式)来改变URL,而不触发完整的页面刷新。 - URLSearchParams:现代浏览器提供了
URLSearchParamsAPI,用于方便地解析和操作查询字符串。
const params = new URLSearchParams(location.search);
const id = params.get('id'); // 获取查询参数 'id' 的值
params.set('page', '2'); // 设置参数
console.log(params.toString()); // 输出序列化后的字符串
12.3 navigator对象
它提供了关于浏览器和操作系统的大量信息。其对象属性通常用来确定浏览器的类型。
经典属性:
-
navigator.userAgent:浏览器的用户代理字符串。注意:由于历史原因,这个字符串非常混乱且容易被篡改,不推荐用于精确的浏览器检测。
-
navigator.platform:操作系统平台。navigator.languages:返回浏览器的主语言
现代属性与方法(越来越重要):
-
navigator.onLine:返回布尔值,表示浏览器是否联网。可以监听online和offline事件。
-
navigator.clipboard:剪贴板API。提供了安全、异步的读写剪贴板内容的能力。
// 写入文本到剪贴板
navigator.clipboard.writeText('Hello, Clipboard!').then(() => {
console.log('Text copied to clipboard');
});
-
navigator.mediaDevices:媒体设备API。用于访问摄像头和麦克风。
// 获取用户媒体(摄像头)
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
videoElement.srcObject = stream;
});
-
navigator.serviceWorker:Service Worker API。用于创建可编程的网络代理,是实现PWA(渐进式Web应用)、离线体验和后台同步的核心。navigator.geolocation:地理定位API。用于获取用户设备的地理位置。
12.4 history 对象
它代表了用户的导航历史记录。出于安全考虑,不允许脚本直接访问具体的URL,但可以对其进行操作。
- 传统方法:
-
history.go(n):在历史记录中导航n步(正数前进,负数后退)。history.back():后退一页。history.forward():前进一页。history.length:历史记录栈中的条目数。
现代核心:History API
这是现代单页应用的基石。它允许我们更新URL而不触发页面刷新,并管理应用状态。
-
history.pushState(state, title, url):向历史记录栈添加一条记录。URL改变,但页面不刷新。state是一个与新历史记录条目关联的状态对象。history.replaceState(state, title, url):替换当前历史记录条目。URL改变,但页面不刷新。
示例(SPA路由):
// 当用户点击一个“关于我们”的链接时
function navigateToAbout() {
// 1. 使用 pushState 改变URL
history.pushState({ page: 'about' }, 'About Us', '/about');
// 2. 然后,通过你自己的路由逻辑,动态地更新页面内容(例如,渲染About组件)
renderAboutPage();
}
// 监听 popstate 事件,当用户点击前进/后退按钮时触发
window.addEventListener('popstate', (event) => {
// 根据 event.state 中的信息,渲染对应的页面
if (event.state && event.state.page === 'about') {
renderAboutPage();
} else {
renderHomePage();
}
});更多推荐



所有评论(0)