目录
12.1 window 对象
BOM 的核心是 window 对象,表示浏览器的实例。window 对象在浏览器中有两重身份,一个是 ECMAScript 中的 Global 对象,另一个就是浏览器窗口的 JavaScript 接口。
12.1.1 Global 作用域
因为 window 对象被复用为 ECMAScript 的 Global 对象,所以通过 var 声明的所有全局变量和函 数都会变成 window 对象的属性和方法。比如:
var age = 29;
var sayAge = () => alert(this.age);
alert(window.age); // 29
sayAge(); // 29
window.sayAge(); // 29
这里,变量 age 和函数 sayAge()被定义在全局作用域中,它们自动成为了 window 对象的成员。 因此,变量 age 可以通过 window.age 来访问,而函数 sayAge()也可以通过 window.sayAge()来访问。 因为 sayAge()存在于全局作用域,this.age 映射到 window.age,所以就可以显示正确的结果了。 如果在这里使用 let 或 const 替代 var,则不会把变量添加给全局对象:
let age = 29;
const sayAge = () => alert(this.age);
alert(window.age); // undefined
sayAge(); // undefined
window.sayAge(); // TypeError: window.sayAge is not a function
12.1.2 窗口关系
top 对象始终指向最上层(最外层)窗口,即浏览器窗口本身。而 parent 对象则始终指向当前窗 口的父窗口。如果当前窗口是最上层窗口,则 parent 等于 top(都等于 window)。最上层的 window 如果不是通过 window.open()打开的,那么其 name 属性就不会包含值,本章后面会讨论。 还有一个 self 对象,它是终极 window 属性,始终会指向 window。实际上,self 和 window 就 是同一个对象。之所以还要暴露 self,就是为了和 top、parent 保持一致。 这些属性都是 window 对象的属性,因此访问 window.parent、window.top 和 window.self 都可以。这意味着可以把访问多个窗口的 window 对象串联起来,比如 window.parent.parent。
12.1.3 窗口位置与像素比
window 对象的位置可以通过不同的属性和方法来确定。现代浏览器提供了 screenLeft 和 screenTop 属性,用于表示窗口相对于屏幕左侧和顶部的位置 ,返回值的单位是 CSS 像素。
可以使用 moveTo()和 moveBy()方法移动窗口。这两个方法都接收两个参数,其中 moveTo()接 收要移动到的新位置的绝对坐标 x 和 y;而 moveBy()则接收相对当前位置在两个方向上移动的像素数。 比如:
// 把窗口移动到左上角
window.moveTo(0,0);
// 把窗口向下移动 100 像素
window.moveBy(0, 100);
// 把窗口移动到坐标位置(200, 300)
window.moveTo(200, 300);
// 把窗口向左移动 50 像素
window.moveBy(-50, 0);
// 依浏览器而定,以上方法可能会被部分或全部禁用
CSS 像素是 Web 开发中使用的统一像素单位。这个单位的背后其实是一个角度:0.0213°。如果屏 幕距离人眼是一臂长,则以这个角度计算的 CSS 像素大小约为 1/96 英寸。这样定义像素大小是为了在 不同设备上统一标准。比如,低分辨率平板设备上 12 像素(CSS 像素)的文字应该与高清 4K 屏幕下 12 像素(CSS 像素)的文字具有相同大小。这就带来了一个问题,不同像素密度的屏幕下就会有不同的 缩放系数,以便把物理像素(屏幕实际的分辨率)转换为 CSS 像素(浏览器报告的虚拟分辨率)。
举个例子,手机屏幕的物理分辨率可能是 1920×1080,但因为其像素可能非常小,所以浏览器就需 要将其分辨率降为较低的逻辑分辨率,比如 640×320。这个物理像素与 CSS 像素之间的转换比率由 window.devicePixelRatio 属性提供。对于分辨率从 1920×1080 转换为 640×320 的设备,window. devicePixelRatio 的值就是 3。这样一来,12 像素(CSS 像素)的文字实际上就会用 36 像素的物理 像素来显示。 window.devicePixelRatio 实际上与每英寸像素数(DPI,dots per inch)是对应的。DPI 表示单 位像素密度,而 window.devicePixelRatio 表示物理像素与逻辑像素之间的缩放系数。
12.1.4 窗口大小
所有现代浏览器都支持 4 个属性: innerWidth、innerHeight、outerWidth 和 outerHeight。outerWidth 和 outerHeight 返回浏 览器窗口自身的大小(不管是在最外层 window 上使用,还是在窗格中使用)。innerWidth 和 innerHeight 返回浏览器窗口中页面视口的大小(不包含浏览器边框和工具栏)。 document.documentElement.clientWidth 和 document.documentElement.clientHeight 返回页面视口的宽度和高度。
12.1.5 视口位置
略
12.1.6 导航与打开新窗口
window.open()方法可以用于导航到指定 URL,也可以用于打开新浏览器窗口。这个方法接收 4 个参数:要加载的 URL、目标窗口、特性字符串和表示新窗口在浏览器历史记录中是否替代当前加载页 面的布尔值。通常,调用这个方法时只传前 3 个参数,最后一个参数只有在不打开新窗口时才会使用。
如果 window.open()的第二个参数是一个已经存在的窗口或窗格(frame)的名字,则会在对应的 窗口或窗格中打开 URL。下面是一个例子:
// 与<a href="http://www.wrox.com" target="topFrame"/>相同
window.open("http://www.wrox.com/", "topFrame");
12.1.7 定时器
JavaScript 在浏览器中是单线程执行的,但允许使用定时器指定在某个时间之后或每隔一段时间就 执行相应的代码。setTimeout()用于指定在一定时间后执行某些代码,而 setInterval()用于指定 每隔一段时间执行某些代码。
setTimeout()方法通常接收两个参数:要执行的代码和在执行回调函数前等待的时间(毫秒)。第 一个参数可以是包含 JavaScript 代码的字符串(类似于传给 eval()的字符串)或者一个函数,比如:
// 在 1 秒后显示警告框
setTimeout(() => alert("Hello world!"), 1000);
第二个参数是要等待的毫秒数,而不是要执行代码的确切时间。JavaScript 是单线程的,所以每次 只能执行一段代码。为了调度不同代码的执行,JavaScript 维护了一个任务队列。其中的任务会按照添 加到队列的先后顺序执行。setTimeout()的第二个参数只是告诉 JavaScript 引擎在指定的毫秒数过后 把任务添加到这个队列。如果队列是空的,则会立即执行该代码。如果队列不是空的,则代码必须等待 前面的任务执行完才能执行。
调用 setTimeout()时,会返回一个表示该超时排期的数值 ID。这个超时 ID 是被排期执行代码的 唯一标识符,可用于取消该任务。要取消等待中的排期任务,可以调用 clearTimeout()方法并传入超 时 ID,如下面的例子所示:
// 设置超时任务
let timeoutId = setTimeout(() => alert("Hello world!"), 1000);
// 取消超时任务
clearTimeout(timeoutId);
只要是在指定时间到达之前调用 clearTimeout(),就可以取消超时任务。在任务执行后再调用 clearTimeout()没有效果。
12.1.8 系统对话框
alert、confirm、prompt
12.2 location 对象
location 是最有用的 BOM 对象之一,提供了当前窗口中加载文档的信息,以及通常的导航功能。 这个对象独特的地方在于,它既是 window 的属性,也是 document 的属性。也就是说, window.location 和 document.location 指向同一个对象。location 对象不仅保存着当前加载文 档的信息,也保存着把 URL 解析为离散片段后能够通过属性访问的信息。
假设浏览器当前加载的 URL 是 http://foouser:barpassword@www.wrox.com:80/WileyCDA/?q= javascript#contents,location 对象的内容如下表所示。
12.2.1 查询字符串
略
12.2.2 操作地址
可以通过修改 location 对象修改浏览器的地址。首先,最常见的是使用 assign()方法并传入一 个 URL,如下所示:
location.assign("http://www.wrox.com");
这行代码会立即启动导航到新 URL 的操作,同时在浏览器历史记录中增加一条记录。
修改 location 对象的属性也会修改当前加载的页面。其中,hash、search、hostname、pathname 和 port 属性被设置为新值之后都会修改当前 URL,如下面的例子所示:
// 假设当前 URL 为 http://www.wrox.com/WileyCDA/
// 把 URL 修改为 http://www.wrox.com/WileyCDA/#section1
location.hash = "#section1";
// 把 URL 修改为 http://www.wrox.com/WileyCDA/?q=javascript
location.search = "?q=javascript";
// 把 URL 修改为 http://www.somewhere.com/WileyCDA/
location.hostname = "www.somewhere.com";
// 把 URL 修改为 http://www.somewhere.com/mydir/
location.pathname = "mydir";
// 把 URL 修改为 http://www.somewhere.com:8080/WileyCDA/
location.port = 8080;
除了 hash 之外,只要修改 location 的一个属性,就会导致页面重新加载新 URL。
在以前面提到的方式修改 URL 之后,浏览器历史记录中就会增加相应的记录。当用户单击“后退” 按钮时,就会导航到前一个页面。如果不希望增加历史记录,可以使用 replace()方法。这个方法接 收一个 URL 参数,但重新加载后不会增加历史记录。调用 replace()之后,用户不能回到前一页。
setTimeout(() => location.replace("http://www.wrox.com/"), 1000);
浏览器加载这个页面 1 秒之后会重定向到 www.wrox.com。此时,“后退”按钮是禁用状态,即不能 返回这个示例页面。
最后一个修改地址的方法是 reload(),它能重新加载当前显示的页面。调用 reload()而不传参 数,页面会以最有效的方式重新加载。也就是说,如果页面自上次请求以来没有修改过,浏览器可能会 从缓存中加载页面。如果想强制从服务器重新加载,可以像下面这样给 reload()传个 true:
location.reload(); // 重新加载,可能是从缓存加载
location.reload(true); // 重新加载,从服务器加载
12.3 navigator 对象
navigator 是由 Netscape Navigator 2 最早引入浏览器的,现在已经成为客户端标识浏览器的标准。 只要浏览器启用 JavaScript,navigator 对象就一定存在。但是与其他 BOM 对象一样,每个浏览器都 支持自己的属性。
navigator 对象实现了 NavigatorID 、 NavigatorLanguage 、 NavigatorOnLine 、 NavigatorContentUtils 、 NavigatorStorage 、 NavigatorStorageUtils 、 NavigatorConcurrentHardware、NavigatorPlugins 和 NavigatorUserMedia 接口定义的属性和方法。
下表列出了这些接口定义的属性和方法:
12.3.1 检测插件
略
12.3.2 注册处理程序
略
12.4 screen 对象
indow 的另一个属性 screen 对象,是为数不多的几个在编程中很少用的 JavaScript 对象。这个对 象中保存的纯粹是客户端能力信息,也就是浏览器窗口外面的客户端显示器的信息,比如像素宽度和像 素高度。每个浏览器都会在 screen 对象上暴露不同的属性。下表总结了这些属性。
12.5 history 对象
history 对象表示当前窗口首次使用以来用户的导航历史记录。因为 history 是 window 的属性, 所以每个 window 都有自己的 history 对象。出于安全考虑,这个对象不会暴露用户访问过的 URL, 但可以通过它在不知道实际 URL 的情况下前进和后退。
12.5.1 导航
go()方法可以在用户历史记录中沿任何方向导航,可以前进也可以后退。这个方法只接收一个参数, 这个参数可以是一个整数,表示前进或后退多少步。负值表示在历史记录中后退(类似点击浏览器的“后 退”按钮),而正值表示在历史记录中前进(类似点击浏览器的“前进”按钮)。
// 后退一页
history.go(-1);
// 前进一页
history.go(1);
// 前进两页
history.go(2);
go()有两个简写方法:back()和 forward()。顾名思义,这两个方法模拟了浏览器的后退按钮和 前进按钮:
// 后退一页
history.back();
// 前进一页
history.forward();
12.5.2 历史状态管理
现代 Web 应用程序开发中最难的环节之一就是历史记录管理。用户每次点击都会触发页面刷新的 时代早已过去,“后退”和“前进”按钮对用户来说就代表“帮我切换一个状态”的历史也就随之结束 了。为解决这个问题,首先出现的是 hashchange 事件。HTML5 也为 history 对象增加了方便的状态管理特性。
hashchange 会在页面 URL 的散列变化时被触发,开发者可以在此时执行某些操作。而状态管理 API 则可以让开发者改变浏览器 URL 而不会加载新页面。为此,可以使用 history.pushState()方 法。这个方法接收 3 个参数:一个 state 对象、一个新状态的标题和一个(可选的)相对 URL。
12.6 小结
浏览器对象模型(BOM,Browser Object Model)是以 window 对象为基础的,这个对象代表了浏 览器窗口和页面可见的区域。window 对象也被复用为 ECMAScript 的 Global 对象,因此所有全局变 量和函数都是它的属性,而且所有原生类型的构造函数和普通函数也都从一开始就存在于这个对象之 上。
本章讨论了 BOM 的以下内容。
要引用其他 window 对象,可以使用几个不同的窗口指针。
通过 location 对象可以以编程方式操纵浏览器的导航系统。通过设置这个对象上的属性,可 以改变浏览器 URL 中的某一部分或全部。
使用 replace()方法可以替换浏览器历史记录中当前显示的页面,并导航到新 URL。
navigator 对象提供关于浏览器的信息。提供的信息类型取决于浏览器,不过有些属性如 userAgent 是所有浏览器都支持的。
BOM 中的另外两个对象也提供了一些功能。screen 对象中保存着客户端显示器的信息。这些信息 通常用于评估浏览网站的设备信息。history 对象提供了操纵浏览器历史记录的能力,开发者可以确 定历史记录中包含多少个条目,并以编程方式实现在历史记录中导航,而且也可以修改历史记录。