本系列内容由ZouStrong整理收录
整理自《JavaScript权威指南(第六版)》,《JavaScript高级程序设计(第三版)》
BOM(浏览器对象模型)是一个API,用于访问浏览器的功能,这些功能与任何网页内容无关
初期由于缺少规范,浏览器提供商会按照各自的想法随意去扩展BOM,导致BOM在老版本浏览器中有所不同(BOM现已加入HTML5规范)
整个BOM的最核心对象就是window对象,它表示浏览器的一个实例,window对象有双重角色
后面讲到的location对象、navigator对象、screen对象 、history对象都是window对象的一个属性
window对象的toString()方法返回"[object Window]"字符串,valueOf()方法返回对象本身
在浏览器中,window对象充当了全局对象,因此所有在全局作用域中声明的变量、函数都会变成window对象的属性和方法
var a = 3 ;
function test(){
}
a === window.a //true
test === window.test //true
虽然全局变量会成为window对象的属性,但是和直接在window对象上定义属性有差别:使用var声明的全局变量不能通过delete运算符删除,直接定义的window属性可以
var a = 3 ;
window.b =3 ;
delete a ; //false
delete window.b; //true
另外尝试访问未声明的变量会抛出错误,但是通过查询window对象,则不会
var a=b; //出错,未定义的变量只能进行typeof
var c=window.b //一次属性查询,返回undefined
一个浏览器可以打开多个标签页或者多个窗口,每一个标签页或者窗口都是独立的“浏览上下文”,每一个上下文都是独立的window对象,互不干扰
窗口并不总是和其它窗口完全没有关系,一个窗口或者标签页的脚本可以打开新的窗口或标签页,他们之间是可以相互操作的
此外,当使用iframe嵌套多个文档时,嵌套的文档也有自己的window对象,但是嵌套的浏览上下文之间不是独立的,JavaScript代码可以看到它的祖先或者子孙窗体(尽管会受到同源策略的限制)
当由于同源策略的限制导致窗口之间无法直接交互时,HTML5提供了一个基于事件的消息传输API
使用window.open()方法可以打开(也可能不打开)一个新窗口或者标签页
它会载入指定的URL到新的或者已经存在的窗口中,并返回代表那个窗口的window对象
如果没有传入第三个参数,那么就会打开一个带有全部默认设置(工具栏、地址栏和状态栏等)的新浏览器窗口(或者打开一个新标签页——根据浏览器设置)。在不打开新窗口的情况下,会忽略第三个参数。
window.open("http:a.com", "height=400,width=400,top=10,left=10,resizable=yes");
window.open()方法的返回值代表新窗口的window对象,可以在自己的JavaScript代码中使用这个window对象来引用新创建的窗口(所以说,和使用a链接跳转的页面还是不同的)
var win = window.open();
document.onclick =function(){
win.alert(1)
};
win.alert(2)
win.location="http://www.baidu.com"
在由window.open()创建的窗口中,window对象有一个opener属性,引用的是打开这个窗口的window对象(所以说,两者可以相互引用),在其他窗口中.opener属性为null
var win = window.open('b.html');
win.opener !== null ; //true
win.open().opener === win; //true
window.open()被广告商滥用,所以很多浏览器限制了这个功能(已拦截一个弹出窗口)——只有为了响应鼠标单击这样的用户触发事件的时候,才能安心使用它
open()打开窗口,close()则用于关闭窗口,如果在打开窗口的时候,获得了window对象,则很容易在任意时刻关闭它
var win = window.open('b.html');
win.close();
如果想关闭自己的窗口,则可以直接
window.close();
虽然全局函数可以直接调用,但是由于document上也有close()方法,因此要始终使用window.close(),避免混淆
多数浏览器只允许关闭自己的代码创建的窗口,如果要关闭其他窗口,会让用户进行确认,此外,window.close不能关闭嵌套的窗体
既是一个窗口关闭了,它的window对象依然存在,他的closed属性为true,document为null(文档不能被操作了)
window.open()方法返回代表新窗口的window对象,而新窗口的window对象具有opener属性,指代打开它的原始窗口,这样,两个窗口就可以互相引用,彼此都可以读取到对方的属性或者调用对方的方法
此外,任何窗口或者内联窗体都可以通过window或者self来引用自己的window对象
window === self; //true
窗体可以通过parent属性引用包含窗体的window对象
parent.history.back();
如果一个窗口是顶级窗口或者标签,那么parnet属性就是自身
parent === self;
如果窗体被嵌套了两层,则可以使用parent.parent来引用顶级窗口,
top属性是一个快捷方式,无论嵌套了几层,始终指向顶级窗口,同样的,如果一个窗口是顶级窗口或者标签,那么top属性就是自身
parent属性和top属性可以引用它的祖先窗体,要想引用子孙窗体,可以有很多方法
窗体是通过iframe元素创建的,该元素的contentWindow属性就引用了该窗体的window对象
var iframe = document.getElementById('strong');
var win = iframe.contentWindow; //取得window对象
而该窗体的window对象有一个frameElement属性,反向引用该窗体的iframe元素(顶级窗口的frameElement属性为null)
var iframe = document.getElementById('strong');
var win = iframe.contentWindow; //取得window对象
win.frameElement === iframe;
更简单的,每一个window对象都有一个frames属性,它引用自身包含的所有窗体的window对象
这是一个类数组对象,可以通过数组或者窗体名(iframe的id或者name值)进行索引
window.frames[0]; //第一个子窗体
window.frames[1].frames[2]; //第二个子窗体的第三个子窗体
含有id的元素,可以使用window.id快速获得元素对象,Iframe元素和其它所有元素不同,通过window.id获得的是窗体的window对象,而不是iframe元素对象
每个窗口和窗体都是它自身的JavaScript执行上下文,以各自的window对象作为全局对象
假设一个页面有两个iframe元素,分别是"A"和"B",它们包含的文档来源于一个相同的服务器
窗口A的脚本定义了一个变量
var strong = 3
那么在窗口B中,可以这样引用
parent.A.strong ==3; //true
同样,窗口A声明一个函数
function strong(){}
那么在窗口B中,可以这样调用
parent.A.strong();
如果要频繁调用,可以先保存起来
var f = parent.A.strong;
f();
注意函数的词法作用域,函数的作用域是在定义的时候决定的,而不是调用的时候
分别用于表示浏览器窗口相对于屏幕左边和上边的位置,Firefox则使用
screenY属性(Firefox)
var left = (typeof window.screenLeft == "number") ? window.screenLeft : window.screenX;
var top = (typeof window.screenTop == "number") ? window.screenTop : window.screenY;
注:由于浏览器差异,取得的值在不同浏览器下仍不相同,所以最终结果,就是无法在跨浏览器的条件下取得窗口左边和上边的精确坐标值
将窗口精确地移动到一个新位置,这两个方法都接收两个参数,其中moveTo()接收的是新位置的x和y坐标值,而moveBy()接收的是在 x 和 y方向上移动的像素数(都可接收负数)
window.moveTo(0,0); //将窗口移动到屏幕左上角
window.moveBy(0,100); //将窗向下移动100像素
window.moveTo(200,300); //将窗口移动到(200,300)
window.moveBy(-50,0); //将窗口向左移动50像素
注:这两个方法可能会被浏览器禁用(实测,仅Safari和IE支持),且这两个方法都不适用于框架,只能对最外层的window对象使用
IE9+、Safari、Opera和Chrome都提供了
返回浏览器窗口和浏览器内容区的大小
IE8及以下浏览器没有提供取得浏览器窗口尺寸的属性,但是通过DOM提供了内容区(除去任务栏和底栏等)的宽高属性(全浏览器支持)
或者在兼容模式下
这对于手机单页应用和PC单页应用开发来说非常重要,之前一直用各种嵌套的100%
可以调整浏览器窗口大小,这两个方法都接收两个参数,其中resizeTo()接收浏览器窗口的新宽度和新高度,而resizeBy()接收新窗口与原窗口的宽度和高度之差
window.resizeTo(100, 100); //调整到100×100
window.resizeBy(100, 50); //调整到200×150
注:这两个方法可能会被浏览器禁用(仅Safari和IE支持),且这两个方法都不适用于框架,只能对最外层的window对象使用
JavaScript是单线程语言,但它允许通过设置超时值和间歇时间值来调度代码在特定的时刻执行
window对象的setTimeout()和setInterval()方法可以用来注册在指定的时间之后单次或者重复调用的函数,虽然作为window对象的方法,但是他们不会对窗口做什么
setTimeout()用来实现一个函数(或者一段代码)在指定的时间(毫秒)之后运行一次
第一个参数,要执行的代码(可以是JavaScript字符串,也可以是一个函数,但不建议传递字符串!因为可能导致性能损失)
第二个参数,以毫秒表示的时间
setTimeout("alert('Hello world!') ", 1000);
setTimeout(function() {
alert("Hello world!");
}, 1000);
执行完超时调用方法所在行后,立马会执行后续语句(不阻塞),但是直到经过第二个参数指定时间后第一个参数指定的代码却不一定会执行(即使指定0毫秒),因为JavaScript 是一个单线程的,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个JavaScript任务队列。这些任务会按照将它们添加到队列的顺序执行。setTimeout()的第二个参数告诉JavaScript再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行
setTimeout()会返回一个数值ID值(1、2、3……),这个ID是计划执行代码的唯一标识符,可以通过它来取消超时调用
唯一参数,超时调用返回的ID,只要是在指定的时间到达之前调用clearTimeout(),就可以完全取消超时调用
//设置超时调用
var timeoutId = setTimeout(function() {
alert("Hello world!");
}, 1000);
//注意:把它取消
clearTimeout(timeoutId);
注:超时调用的代码都是在全局作用域中执行的,因此函数中this的值在非严格模式下指向window对象,在严格模式下是undefined(详见函数this部分)
setInterval()用来实现一个函数(或者一段代码)以指定的时间间隔(毫秒)重复调用
第一个参数,要执行的代码(可以是JavaScript字符串,也可以是一个函数,但不建议传递字符串!因为可能导致性能损失)
第二个参数,以毫秒表示的时间
setInterval ("alert('Hello world!') ", 10000);
setInterval (function() {
alert("Hello world!");
}, 10000);
经过第二个参数指定时间后代码也不一定会执行
调用setInterval()方法同样也会返回一个间歇调用ID,该ID用于在将来某个时刻取消间歇调用
唯一参数,间歇调用返回的ID
var num = 0;
var max = 10;
var intervalId = null;
function incrementNumber() {
num++;
//如果执行次数达到了max设定的值,则取消后续尚未执行的调用
if (num == max) {
clearInterval(intervalId);
alert("Done");
}
}
intervalId = setInterval(incrementNumber, 500);
取消间歇调用的重要性要远远高于取消超时调用,因为在不加干涉的情况下,间歇调用将会一直执行到页面卸载
//同样的作用,可使用超时调用重现
var num = 0;
var max = 10;
function incrementNumber() {
num++;
//如果执行次数未达到max设定的值,则设置另一次超时调用
if (num < max) {
setTimeout(incrementNumber, 500);
} else {
alert("Done");
}
}
setTimeout(incrementNumber, 500);
HTML5规范(IE不支持)允许这两个方法接收后续多个参数,他们会被当做函数的参数传递进去
在使用超时调用时,没有必要跟踪超时调用ID,因为每次执行代码之后,如果不再设置另一次超时调用,调用就会自行停止。一般认为,使用超时调用来模拟间歇调用的是一种最佳模式。在开发环境下,很少使用真正的间歇调用,原因是后一个间歇调用可能会在前一个间歇调用结束之前启动。而像前面示例中那样使用超时调用,则完全可以避免这一点
所以,最好不要使用间歇调用
接受一个字符串参数,向用户显示一个系统对话框,其中包含参数文本和一个"确定"按钮
警告对话框向用户显示一些他们无法控制的消息,例如错误消息,而用户只能在看完消息后关闭对话框
接受一个字符串参数,向用户显示一个系统对话框,其中包含参数文本、"确定"按钮和"取消"按钮
确认对话框除了向用户显示消息, 还有返回值,用户点击确认,返回true;点击取消,返回false
接受一个字符串参数,表示对用户的提示信息,还接受可选的第二个字符串参数,表示文本输入框的默认值
向用户显示一个系统对话框,用于提示用户输入一些文本
提示对话框提示用户输入信息,如果用户单击了确定按钮,则返回文本输入域的值(总是以字符形式返回);如果用户单击了取消或者"X",则该方法返回null
........
两次独立的用户操作分别打开两个警告框,那么这两个警告框中都不会显示复选框。而如果是同一次用户操作会生成两个警告框,那么第二个警告框中就会显示复选框
window对象上的onerror属性是一个事件处理程序,当错误传递到window上时会触发,并且默认把错误信息输出到控制台
如果显示给这个属性设置一个函数,在错误发生时,就会调用该函数
之所以把这个事件放在这里将,是因为它和后面讲到的时间处理程序不同,它的事件处理程序接收的不是事件对象,而是三个参数
onerror事件处理程序是早期产物,现在更常用try/catch来处理异常了
如果HTML元素有一个id值,并且在window上没有与此同名的属性,那么window对象会隐式获得一个属性,属性名就是id的值,属性值就是当前元素对象
window是全局对象,这就意味着在HTML中使用的id属性会成为可以被脚本访问的全局变量
<p id="pra">strong</p>
<script>
window.pra.innerText; //"strong"
</script>
但是,如果window对象已经具有同名的内置属性,就不会隐式添加属性了
<p id="history">strong</p>
如果在代码中声明了一个同名的全局变量,并且在window属性访问之前,那么显式生命的变量会覆盖隐式的元素变量
<div id="strong">strong </div>
<script>
alert(window.strong.innerHTML) //"strong"
var strong=1; //不管变量声明提升?
</script>
<script>
var strong=1;
alert(window.strong.innerHTML) //"undefined
</script>
注:元素ID作为全局变量的隐式应用是浏览器遗留的怪癖,但是浏览器都支持,不过最好还是通过document.getElementById()来获取
window对象的location属性指代的是location对象,提供了与当前窗口中加载的URL有关的信息
location 对象是很特别的一个对象,因为它也是document对象的属性
window.location === document.location
http://www.strong.com:8080/news/index.html?name=strong&age=18#name
location对象的方法都和页面跳转和重载有关
location对象的valueOf()方法返回对象本身
location对象的toString()方法返回location.href的值,因此可以在会隐式调用toString()的情况下,使用location代替location.href
接收一个URL字符串,实现页面的跳转
location.assign("http://www.strong.com");
为window.location或者location.href设置URL值,也会跳转,其实就是在底层调用了assign()方法
这三种跳转方法都会生成历史记录,用户可通过回退按钮回到前一个页面
接收一个URL字符串,实现页面的跳转,但是在跳转之前会从历史记录中将当前文档删除(也可以理解为覆盖当前文档的历史记录),因此使用replace()方法,不能通过回退按钮回退到前一个页面
location.replace("http://www.strong.com");
可以通过document.referrer回到上一个页面
方式1:使用meta标签,指定秒数后跳转
<meta http-equiv="refresh" content="2; url=http://www.ba.com/" />
方式2:location对象
location = "http://www.abc.com";
window.location = "http://www.abc.com";
document.location = "http://www.abc.com";
方式3:location.href属性
location.href = "http://www.abc.com";
方式4:location.assign()方法
location.assign("http://www.abc.com");
方式5:location.replace()方法
location.replace("http://www.abc.com"); //不能通过回退按钮返回
方式6:window.open()方法
window.open("http://www.abc.com"); //如不果怕被阻止,你就用吧
纯粹的片段标识符不会使页面跳转,也不会重载页面,只会使页面滚动到某个位置,"#top"是个特殊的例子,如果没有元素的id是top的话,则会滚动到顶部
location.hash = "#top";
location.hash = "#";
重新加载当前显示的页面,可以接收一个布尔值true,表示强制加载
location.reload(); //重新加载(有可能从缓存中加载)
location.reload(true); //重新加载(强制从服务器重新加载)
位于reload()调用之后的代码可能会也可能不会执行,这要取决于网络延迟或系统资源等因素,为此,最好将reload()放在代码的最后一行
除此之外,页面刷新还有很多方法,那就是上面的页面跳转方法——只是把URL换成当前页面的URL即可(可以是写固定的,也可以是JS取的(例如直接:location))
window对象的navigator属性指代的是navigator对象,提供了与浏览器本身有关的信息(这样命名,是为了纪念Netscape的navigator浏览器)
toString()方法返回"[object Navigator]"字符串
valueOf()方法返回对象本身
提供了以下属性
过去,这些属性常用来做浏览器嗅探(浏览器检测),现在不怎么常用了,而是使用能力检测来替代
window对象的screen属性指代的是screen对象,提供了与显示设备有关的信息,它是非标准但却广泛使用的
toString()方法返回"[object Screen]"字符串
valueOf()方法返回对象本身
window对象的history属性指代的是history对象,提供了与浏览历史有关的信息
toString()方法返回"[object History]"字符串
valueOf()方法返回对象本身
保存着历史记录的数量
if (history.length == 0){
//这应该是用户打开窗口后的第一个页面
}
出于安全考虑,脚本不能访问已保存的URL
在历史记录中任意跳转
接受一个参数,一个整数(正或负)或者一个字符串
history.go(-1); //后退一页(相当于单击“后退”按钮)
history.go(1); //前进一页(相当于单击“前进”按钮)
history.go(2); //前进两页
字符串作为参数时,浏览器会跳转到历史记录中包含该字符串的第一个位置——可能后退,也可能前进(哪个位置最近)如果历史记录中不包含该字符串,那么这个方法什么也不做
history.go("wrox.com"); //跳转到最近的wrox.com页面
history.back(); // 相当于 history.go(-1);
history.forward(); // 相当于 history.go(1);
history并不常用,但在创建自定义的“后退”和“前进”按钮,以及检测当前页面是不是用户历史记录中的第一个页面时,还是必须使用它