JavaScript⾼级程序设计:第⼋章
1.window对象——BOM的核⼼
BOM的核⼼对象时window,它表⽰浏览器的⼀个实例。在浏览器中,window对象有双重⾓⾊,它既是通过javascript访问浏览器窗⼝的⼀个接⼝,⼜是ECMAscript规定的global对象。这意味着在⽹页中定义的任何⼀个对象、变量和函数,都以window作为其global对象,因此有权访问parseInt()等⽅法。
(1)全局作⽤域
javascript高级编程pdf下载
由于window对象同时扮演着ECMAScript中global对象的⾓⾊,因此所有在全局作⽤域中声明的变量、函数都会变成window对象的属性和⽅法。来看下⾯的例⼦:
var age = 29 ;
function sayAge(){
alert(this.age);
}
alert(window.age);        //29
sayAge();                //29
window.sayAge();        //29
我们在全局作⽤域中定义了⼀个变量age和⼀个函数sayAge(),它们被⾃动归在了window对象名下。于是,可以通过window.age访问变量age,可以通过window.sayAge()访问函数sayAge()。由于sayAge()存在于全局作⽤域中,因此this.age被映射到window.age,最终显⽰的仍然是正确的。
抛开全局变量会成为window对象的属性不谈,定义全局变量与在window对象上直接定义属性还是有⼀点差别的:全局变量不能通过delete操作符删除,⽽直接在window对象上定义的属性可以。例如:
var age = 29;
//在IE < 9时抛出错误,在其他所有浏览器中都返回false
delete window.age;
/
/在IE < 9时抛出错误,在其他所有浏览器中都返回true
lor ;    //returns true
alert(window.age) ;      //29
lor) ;      //undefined
刚才使⽤var语句添加的window属性有⼀个名为[[Configurable]]的特性,这个特性的值被设置为false,因此这样定义的属性不可以通过delete操作符删除。IE8及更早版本在遇到使⽤delete删除window属性的语句时,不管该属性最初是如何创建的,都会抛出错误,以⽰警告。
另外,还要记住⼀件事:尝试未访问声明的变量会抛出错误,但是通过查询window对象,可以知道某个可能未声明的变量是否存在。例如:
//这⾥会抛出错误,因为oldValue未定义
var newValue = oldValue ;
//这⾥不会抛出错误,因为这是⼀次属性查询
/
/newValue的值是undefined
var newValue = window.oldValue ;
(2)窗⼝关系及框架
如果页⾯中包含框架,则每个框架都有⾃⼰的window对象,并且保存在frames集合中。在frames集合中,可以通过数值索引或者框架名称来访问相应的window对象。每个window对象都有⼀个name属性,其中包含框架的名称。下⾯是⼀个包含框架的页⾯:
<html>
<head>
<title>Frameset Example</title>
</head>
<frameset rows=”160 , * ”>
<frame src=”frame.htm” name=”topFrame”>
<frameset cols=”50%,50%”>
<frame src=”anotherframe.htm” name=”leftFrame”>
<frame src=”yetanotherframe.htm” name=”rightFrame”>
</frameset>
</frameset>
</html>
以上代码创建了⼀个框架集,其中⼀个框架居上,两个框架居下。对这个例⼦⽽⾔,可以通过window.frames[0]或者
window.frames[“topFrame”]来引⽤上⽅的框架。
(3)窗⼝位置
⽤来确定和修改window对象位置的属性和⽅法有很多。IE、Safari、Opera和Chrome都提供了screenLeft和screenTop属性,分别⽤于表⽰窗⼝相对于屏幕左边和上边的位置。Firefox则在screenX和
screenY属性中提供相同的窗⼝位置信息,Safari和Chrome也同时⽀持这两个属性。Opera虽然也⽀持screenX和screenY属性,但与screenLeft和screenTop属性并不对应,因此建议⼤家不要再Opera中使⽤它们。使⽤下列代码可以跨浏览器取得窗⼝左边和上边的位置。
var leftPos = (typeof window.screenLeft==”number”) ?
window.screenLeft : window.screenX ;
var topPos = (typeof window.screenTop==”number”) ?
window.screenTop : window.screenY ;
这个例⼦运⽤⼆元操作符⾸先确定screenLeft和screenTop属性是否存在,如果是(在IE、Safari、Opera和Chrome中),则取得这两个属性的值。如果不存在,则取得screenX和screenY的值。
使⽤moveTo()和moveBy()⽅法倒是有可能将窗⼝精确地移动到⼀个新位置。这两个⽅法都接收两个参数,其中moveTo()接受的是新位置的x和y坐标值,⽽moveBy()接受的是在⽔平和垂直⽅向上移动的像素数。下⾯来看⼏个例⼦:
//将窗⼝移动到屏幕左上⾓
//将窗⼝向下移动100像素
//将窗⼝向左移动50像素
需要注意的是,这两个⽅法可能会被浏览器禁⽤;⽽且,在Opera和IE7中默认就是禁⽤的。
(4)窗⼝⼤⼩
跨浏览器确定⼀个窗⼝⼤⼩不是⼀件简单的事。IE9+、Firefox、Safari、Opera和Chrome均为此提供了4个属性:innerWidth、innerHeight、outerWidth和outerHeight。在IE9+、Safari和Firefox中,outerWidth和outerHeight返回浏览器窗⼝本⾝的尺⼨。在Opera中,这两个属性的值表⽰页⾯视图容器的⼤⼩。⽽innerWidth和innerHeight则表⽰该容器中页⾯视图区的⼤⼩。在chrome中,outerWidth、outerHeight与innerWidth、innerHeight返回相同的值,即视⼝⼤⼩⽽⾮浏览器窗⼝⼤⼩。
IE8及更早版本没有提供取得当前浏览器窗⼝尺⼨的属性;不过,它通过DOM提供了页⾯可见区域的相关信息。
在IE、Firefox、Safari、Opera和Chrome中,document.documentElement.clientWidth和document.documentElement.clientHeight中保存了页⾯视⼝的信息。在IE6中,这些属性必须在标准模式下才有效;如果是混杂模式,就必须通过document.body.clientWidth和document.body.clientHeight取得相同信息。⽽对于混杂模式下的Chrome,则⽆论通过document.documentElement还是document.body中的clientWidth和clientHeight属性,都可以取得视⼝的⼤⼩。
虽然最终⽆法确定浏览器窗⼝本⾝的⼤⼩,但却可以取得页⾯视⼝的⼤⼩,如下所⽰
var pageWidth = window.innerWidth , pageHeight = window.innerHeight ;
if(typeof pageWidth != “number”){
if(documentpatMode == “CSS1Compat”){
pageWidth = document.documentElement.clientWidth ;
pageHeight = document.documentElement.clientHeight ;
} else {
pageWidth = document.body.clientWidth ;
pageHeight = document.body.clientHeight ;
}
}
在以上代码中,我们⾸先将window.innerWidth和window.innerHeight的值分别赋给了pageWidth和pageHeight。然后检查pageWidth 中保存的是不是⼀个数值;如果不是,则通过检查documentpatMode来确定页⾯是否处于标准模式。如果是,则分别使⽤document.documentElement.clientWidth和document.documentElement.clientHeight的值。否则,就⽤document.body.clientWidth和document.body.clientHeight的值。
(5)导航和打开窗⼝
使⽤window.open()⽅法既可以导航到⼀个特定的URL,也可以打开⼀个新的浏览器窗⼝。这个⽅法可以接受4个参数:要加载的URL、窗⼝⽬标、⼀个特性字符串以及⼀个表⽰新页⾯是否取代浏览器历
史记录中当前加载页⾯的布尔值。通常只需传递第⼀个参数,最后⼀个参数只在不打开新窗⼝的情况下使⽤。
如果为window.open()传递了第⼆个参数,⽽且该参数是已有窗⼝或框架的名称,那么就会在具有该名称的窗⼝或框架中加载第⼀个参数指定的URL。看下⾯的例⼦:
//等同于< a href=”www.wrox” target=”topFrame”></a>
window.open(”www.wrox/” , “topFrame”);
调⽤这⾏代码,就如同⽤户单击了href属性为”www.wrox/,target”属性为”topFrame”的链接。
①弹出窗⼝
如果给window.open()传递的第⼆个参数并不是⼀个已经存在的窗⼝或框架,那么该⽅法就会根据在第三个参数位置上传⼊的字符串创建⼀个新窗⼝或新标签页。如果没有传⼊第三个参数,那么就会打开⼀个带有全部默认设置的新浏览器窗⼝。在不打开窗⼝的情况下,会忽略第三个参数。第三个参数是⼀个逗号分隔的设置字符串,表⽰在新窗⼝中都显⽰哪些特性。window.open()⽅法会返回⼀个指向新窗⼝的引⽤。引⽤的对象与其他window对象⼤致相似,但我们可以对其进⾏更多控制。例如,有些浏览器在默认情况下可能不允许我们针对主浏览器窗⼝调整⼤⼩或移动位置,但却允许我们针对通过window.
open()创建的窗⼝调整⼤⼩或移动位置。
调⽤close()⽅法可以关闭新打开的窗⼝。
wroxWin.close();
但是,这个⽅法仅适⽤于通过window.open()打开的弹出窗⼝。对于浏览器的主窗⼝,如果没有得到⽤户的允许是不能关闭它的。不过,弹出窗⼝倒是还可以调⽤top.close()在不经⽤户允许的情况下关闭⾃⼰。弹出窗⼝关闭之后,窗⼝的引⽤仍然还在,但除了像下⾯这样检测其closed属性之外,已经没有其他⽤途了。
wroxWin.close();
alert(wroxWin.closed);    //true
新创建的window对象有⼀个opener属性,其中保存着打开它的原始窗⼝对象。这个属性只在弹出窗⼝中的最外层window对象(top)中有定义,⽽且指向调⽤window.open()窗⼝或框架。例如:
var wroxWin = window.open(“www.wrox/”,”wroxWindow”,
”height=400,width=400,top=10,left=10,resizable=yes”);
alert(wroxWin.opener==window);    //true
虽然弹出窗⼝中有⼀个指针指向打开它的原始窗⼝,但原始窗⼝中并没有这样的指针指向弹出窗⼝。窗⼝并不跟踪记录它们打开的窗⼝,因此我们只能在必要的时候⾃⼰来⼿动实现跟踪。
②安全限制
由于⼴告商在⽹上使⽤弹出窗⼝到达了肆⽆忌惮的程度,因此浏览器开始对弹出窗⼝配置⽅⾯增加限制。
③弹出窗⼝屏蔽程序
⼤多数浏览器都内置有弹出窗⼝屏蔽程序,⽽没有内置此类程序的浏览器,也可以安装Yahoo!Toolbar等带有内置屏蔽程序的实⽤⼯具。在弹出窗⼝屏蔽时,就应该考虑两种可能性如果是浏览器内置的屏蔽程序阻⽌的弹出窗⼝,那么window.open()很可能会返回null。此时,只要检测这个返回的值就可以确定弹出的窗⼝是否被屏蔽了,如下⾯的例⼦所⽰:
var wroxWin = window.open(“www.wrox”,”_blank”);
if(wroxWin == null){
alert(“The popup was blocked!”);
}
如果是浏览器扩展或其他程序阻⽌的弹出窗⼝,那么window.open()通常会抛出⼀个错误。因此,要想正确地检测出弹出窗⼝是否被屏蔽,必须在检测返回值的同时,将对window.open()的调⽤封装在⼀个try-catch块中。如下所⽰:
var blocked = false ;
try {
var wroxWin = window.open(“www.wrox”,“_blank”);
if (wroxWin == null){
blocked = true ;
}
} catch (ex){
blocked = true ;
}
if (blocked){
alert(“The popup was blocked!”);
}
在任何情况下,以上代码都可以检测出调⽤window.open()打开的弹出窗⼝是不是被屏蔽了。但要注意的是,检测弹出窗⼝是否被屏蔽只是⼀⽅⾯,它并不会阻⽌浏览器显⽰与屏蔽的弹出窗⼝有关的消息。
(6)间歇调⽤和超时调⽤
javascript是单线程语⾔,但它允许通过设置超时值和间歇时间值来调度代码在特定的时刻执⾏。前者是在指定的时间过后执⾏代码,⽽后者则是每隔指定的时间就要执⾏⼀次代码。
超时调⽤需要使⽤window对象的setTimeout()⽅法,它接受两个参数:要执⾏的代码和以毫秒表⽰的
时间。其中,第⼀个参数可以是⼀个包含javascript代码的字符串,也可以是⼀个函数。例如,下⾯对setTimeout()的两次调⽤都会在⼀秒钟后显⽰⼀个警告框。
//不建议传递字符串!
setTimeout(“alert(‘Hello world!’)” , 1000);
alert(“Hello world!”);
} ,1000);
虽然这两种调⽤⽅式都没有问题,但由于传递字符串可能导致性能损失,因此不建议以字符串作为第⼀个参数。
第⼆个参数是⼀个表⽰等待多长时间的毫秒数,但经过该时间后指定的代码不⼀定会执⾏。javascript是⼀个单线程序的解释器,因此⼀定时间内只能执⾏⼀段代码。为了控制要执⾏的代码,就有⼀个javascript任务队列。这些任务会按照将它们添加到队列的顺序执⾏。setTimeout()的第⼆个参数告诉javascript再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会⽴即执⾏;如果队列不是空的,那么它就要等前⾯的代码执⾏完了之后再执⾏。
调⽤setTimeout()之后,该⽅法会返回⼀个数值ID,表⽰超时调⽤。这个超时调⽤ID是计划执⾏代码的唯⼀标识符,可以通过它来取消超时调⽤。要取消尚未执⾏的超时调⽤计划,可以调⽤clearTimeout()⽅法并将相应的超时调⽤ID作为参数传递给它,如下所⽰:
//设置超时调⽤
var timeoutId = setTimeout(function(){
alert(“Hello world!”);
},1000);
//注意:把它取消
clearTimeout(timeoutId);
只要是在指定的时间尚未过去之前调⽤clearTimeout()就可以完全取消超时调⽤。前⾯的代码在设置超时调⽤之后马上⼜调⽤了clearTimeout(),结果就跟什么也没有发⽣⼀样。
间歇调⽤与超时调⽤类似,只不过它会按照指定的时间间隔重复执⾏代码,直⾄间歇调⽤被取消或者页
⾯被卸载。设置间歇调⽤的⽅法是setInterval(),它接受的参数与setTimeout()相同;要执⾏的代码和每次执⾏前需要等待的毫秒数。下⾯来看⼀个例⼦:
//不建议传递字符串!
setInterval(“alert(‘Hello world!’)” , 10000);
//推荐的调⽤⽅式
setInterval (function(){
alert(“Hello world!”);
} , 10000);
调⽤setInterval()⽅法同样也会返回⼀个间歇调⽤ID,该ID可⽤于在将来某个时刻取消间歇调⽤。要取消尚未执⾏的间歇调⽤,可以使⽤clearInterval()⽅法并传⼊相应的间歇调⽤ID。取消间歇调⽤的重要性要远远⾼于取消超时调⽤,因为在不加⼲涉的情况下,间歇调⽤将会⼀直执⾏到页⾯卸载。以下是⼀个常见的使⽤间歇调⽤的例⼦。
var num = 0 ;
var max = 10 ;
var intervalId = null ;
function incrementNumber() {
num++ ;
// 如果执⾏次数达到了max设定的值,则取消后续尚未执⾏的调⽤
if ( num == max )  {
clearInterval(intervalId);
alter(“Done”);
}
}
intervalId = setInterval(incrementNumber , 500);
在这个例⼦中,变量num每半秒钟递增⼀次,当递增到最⼤值时就会取消先前设定的间歇调⽤。这个模式也可以⽤超时调⽤来实现,如下所⽰:
var num = 0 ;
var max = 10 ;
function incrementNumber() {
num++ ;
//如果执⾏次数未达到max设定的值,则设置另⼀次超时调⽤
if (num < max){
setTimeout(incrementNumber ,500);
} else {
alert(“Done”);
}
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。