BOM(Browser Object Model)的核心是window对象,表示浏览器实例,window对象在浏览器中有两层身份,一是Global对象,另一个就是浏览器窗口的Javascript接口。这意味着网页中定义的所有对象、变量和函数都以window作为Global对象,都可以访问其上定义的ParseInt()等方法。
一、窗口大小
所有现代浏览器都支持4个属性:innerWidth、innerHeight、outerWidth、outerHeight。outerWidth,outerHeight返回浏览器窗口自身的大小,innerWidth,innerHeight返回窗口中页面的大小(不包括窗口边框,工具栏等)。
document.documentElement.clientWidth和document.documentElement.clientHeight返回页面视口的宽度和高度。
二、窗口位置
浏览器窗口尺寸通常无法满足完整显示整个页面,为此用户可以通过滚动在有限的视口中查看文档。度量文档相对于窗口滚动距离的属性有两对:window.pageXoffset/window.scrollX和window.pageYoffset/window.scrollY。同时可以使用scroll(),scrollTo(),scrollBy()方法滚动页面。这三个方法都接受表示相对视口距离的x和y坐标,这两个参数在前两个方法表示滚动到的坐标,在后一个表示相对滚动的距离。它们也能接收一个ScrollToOptions字符串,除了提供偏移值,还可以通过behavior属性提供浏览器是否平缓滑动。

// 相对于当前视口向下滚动 100 像素
 window.scrollBy(0, 100);
// 相对于当前视口向右滚动 40 像素
 window.scrollBy(40, 0);
// 滚动到页面左上角 
window.scrollTo(0, 0);
// 滚动到距离屏幕左边及顶边各 100 像素的位置
 window.scrollTo(100, 100);
// 正常滚动
window.scrollTo({ 
left: 100, top: 100,
behavior: 'auto'
}); // 平滑滚动
window.scrollTo({ 
left: 100, top: 100,
behavior: 'smooth' });

三、导航
window.open()方法可以用于导航到指定的URL,也可以用于打开新的浏览器窗口。这个方法接受4个参数:要加载的URL,目标窗口,特性字符串和表示新窗口在浏览器历史中是否替代当前加载页的布尔值。通常传入前3个参数,最后一个只有在不打开新窗口时才会使用。如果第二个参数是一个已经存在的窗口或窗格名,则会在对应的窗口或窗格打开URL。

window.open("http://www.baidu.com/", "topFrame");

执行这行代码的结果和用户点击了一个herf属性为“baidu.com”,target属性为"topFrame"的链接。如果有名为"topFrame"的窗口,则会在窗口中打开URL,否则会创建一个名为"topFrame"的窗口并打开URL。
四、定时器
Javascript在浏览器中是单线程执行的,但允许使用定时器指定某段代码在一段时间后或每隔一段时间执行。setTimeout()用于在一段时间后执行代码,setInterval()用于指定每隔一段时间执行代码。
setTimeout通常接收两个参数,需要执行的代码和间隔的毫秒数。javascript是单线程的,所以每次只能执行一段代码,为了调度不同代码的执行,JavaScript维护了一个任务队列。其中的任务会按照添加到队列中的顺序执行。setTimeout告诉JavaScript引擎将其中的代码在规定毫秒数后加入任务队列。如果队列是空的,则会立即执行该代码,如果队列不为空则必须等到前面的任务都完成才能执行。在调用setTimeout时会返回一个表示该超时排期的数值ID,这个ID是该定时器的唯一标识符,可以调用clearTimeout()方法并传入该ID对定时器进行取消操作。

// 设置超时任务
let timeoutId = setTimeout(() => alert("Hello world!"), 1000); 
// 取消超时任务
clearTimeout(timeoutId);

setInterval()与 setTimeout()的使用方法类似,只不过指定的任务会每隔指定时间就执行一次,直到取消循环定时或者页面卸载。setInterval()同样可以接收两个参数:要执行的代码以及把下一次执行定时代码的任务添加到队列要等待的时间。这里的关键点是,第二个参数,也就是间隔时间,指的是向队列添加新任务之前等待的时间。浏览器不关心这个任务什么时候执行或者执行要花多长时间。由此可看出,执行时间短、非阻塞的回调函数比较适合 setInterval()。
setInterval()方法也会返回一个循环定时 ID,可以用于在未来某个时间点上取消循环定时。要取消循环定时,可以调用 clearInterval()并传入定时ID。相对于 setTimeout()而言,取消定时的 能力对setInterval()更加重要。毕竟,如果一直不管它,那么定时任务会一直执行到页面卸载。

let num = 0, intervalId = null; let max = 10;
let incrementNumber = function() { 
    num++;
// 如果达到最大值,则取消所有未执行的任务 
    if (num == max) {
        clearInterval(intervalId); alert("Done");
    } 
}
intervalId = setInterval(incrementNumber, 100);

这个例子也可以使用setTimeout实现:

let num = 0; let max = 10;
let incrementNumber = function() { 
    num++;
// 如果还没有达到最大值,再设置一个定时器任务 
    if (num < max) {
        setTimeout(incrementNumber, 100); 
    } else {
        alert("Done"); 
        } 
}
setTimeout(incrementNumber, 100);

两者都能实现周期性调度,但在生产中却基本上使用setTimeout嵌套的方式,为什么呢?
首先,setTimeout比起setInterval灵活得多,我们可以根据当前的调度结果确定下一次调度的时间,因此每次调度的结果都有可能不同,例如:

let delay = 500
timerId = setTimeout(function reuqest(){
    ---发送请求---
    //如果服务器过载则增大请求间隔时间
    if(server overload){
        delay *= 2
    }
    timerId = setTimeout(request,delay)
},delay)

其次steTimeout可以精准的执行延时时间。setInterval在一些情况下甚至会跳过某些应该执行的代码,对比上面两个实现代码,对于setInterval来说:
图片说明

其中的函数调用间隔要比代码设定中短,这是因为setInterval()只负责按规定时间添加代码进入任务队列,所以func的执行所花费的时间“消耗”了一部分间隔时间。也可能出现这种情况,就是 func 的执行所花费的时间比我们预期的时间更长,并且超出了 100 毫秒。在这种情况下,JavaScript 引擎会等待 func 执行完成,然后检查调度程序,如果时间到了,则立即再次执行它。
而这时setTimeout的执行过程:
图片说明
嵌套的setTimeout能保证延时时间的固定,这是因为下一次调用是在上一次调用完成之后才开始的。