14. JavaScript的Window对象(BOM)

172 阅读15分钟

Window对象在客户端JavaScript中扮演核心角色:它是客户端JavaScript程序的全局对象。

1. 计时器setTimeout()

  • Window对象的setTimeout()方法用来实现一个函数在指定的毫秒数之后运行。setTimeout()返回一个值,这个值可以传递给clearTimeout()用于取消这个函数的执行。
  • setInterval()和setTimeout()一样,只不过这个函数会在指定毫秒数的间隔里重复调用。和setTimeout()一样,setInterval()也返回一个值,这个值可以传递给clearInterval(),用于取消后续函数的调用。
  • 除前两个参数(回调函数和延时时间)之外,HTML5规范(除IE之外的所有浏览器)还允许setTimeout()和setInterval()传入额外的参数,并在调用函数时把这些参数传递给回调函数。
/*
 *安排函数f()在未来的调用模式
 *在等待了若干毫秒之后调用f()
 *如果设置了interval并没有设置end参数,则对f()调用将不会停止
 *如果没有设置interval和end,只在若干毫秒后调用f()一次
 *只有指定了f(),才会从start=0的时刻开始
 *注意,调用invoke()不会阻塞,它会立即返回
 */
function invoke(f, start, interval, end) {
  if (!start) start = 0; //默认设置为0毫秒
  if (arguments.length <= 2)
    //单次调用模式
    setTimeout(f, start);
  //若干毫秒后的单次调用模式
  else {
    //多次调用模式
    setTimeout(repeat, start); //在若干毫秒后调用repeat()
    function repeat() {
      //在上一行所示的setTimeout()中调用
      var h = setInterval(f, interval); //循环调用f()
      //在end毫秒后停止调用,前提是end已经定义了
      if (end)
        setTimeout(function() {
          clearInterval(h);
        }, end);
    }
  }
}
invoke(
  function() {
    console.log(new Date());
  },
  3000,
  1000,
  5000
);

image.png 如果以0毫秒的超时时间来调用setTimeout(),那么指定的函数不会立刻执行。相反,会把它放到队列中,等到前面处于等待状态的事件处理程序全部执行完成后,再“立即”调用它

2. 浏览器定位和导航location

Window对象的location属性引用的是Location对象,它表示该窗口中当前显示的文档的URL,并定义了方法来使窗口载入新的文档。

Document对象的location属性也引用到Location对象:

image.png

Document对象也有一个URL属性,是文档首次载入后保存该文档的URL的静态字符串。

image.png

2.1 Location对象解析URL

image.png

  • Location对象的href属性是一个字符串,后者包含URL的完整文本。
  • Location对象的toString()方法返回href属性的值,因此在会隐式调用toString()的情况下,可以使用location代替location.href。

image.png

  • location对象的其他属性——protocol,host,hostname,port,pathname和Search,分别表示URL的各个部分。它们称为“URL分解”属性,
  • Location对象的hash属性返回URL中的“片段标识符”部分(#部分)。
  • search属性也类似,它返回的是问号之后的URL,这部分通常是某种类型的查询字符串(?部分)。
/*
 *这个函数用来解析来自URL的查询串search中的name=value参数对
 *它将name=value对存储在一个对象的属性中,并返回该对象
 */
function urlArgs() {
  var args = {}; //定义一个空对象
  var query = location.search.substring(1); //查找到查询串,并去掉'?'
  var pairs = query.split("&"); //根据"&"符号将查询字符串分隔开
  for (var i = 0; i < pairs.length; i++) {
    //对于每个片段
    var pos = pairs[i].indexOf("="); //查找"name=value"
    if (pos == -1) continue; //如果没有找到的话,就跳过
    var name = pairs[i].substring(0, pos); //提取name
    var value = pairs[i].substring(pos + 1); //提取value
    value = decodeURIComponent(value); //对value进行解码
    args[name] = value; //存储为属性
  }
  return args; //返回解析后的参数
}
var args = urlArgs(); //从URL中解析参数
console.log(args);

image.png

2.2 location载入新的文档

  • Location对象的assign()方法可以使窗口载入并显示你指定的URL中的文档。
  • replace()方法也类似,但它在载入新文档之前会从浏览历史中把当前文档删除。
//如果浏览器不支持XMLHttpRequest对象
//则将其重定向到一个不需要Ajax的静态页面
if (!XMLHttpRequest) location.replace("staticpage.html");
  • Location对象还定义了reload()方法,后者可以让浏览器重新载入当前文档。
  • 使浏览器跳转到新页面的一种更传统的方法是直接把新的URL赋给location属性,还可以把相对URL赋给location,它们会相对当前URL进行解析
location = "http://www.oreilly.com";
  • #top标识符是个特殊的例子:如果文档中没有元素的ID是"top",它会让浏览器跳到文档开始处。
location = "#top"; //跳转到文档的顶部

Location对象的URL分解属性是可写的,对它们重新赋值会改变URL的位置,并且导致浏览器载入一个新的文档(如果改变的是hash属性,则在当前文档中进行跳转)

location.search="?page="+(4+1);//载入下一个页面

3 浏览历史history

Window对象的history属性是用来把窗口的浏览历史用文档和文档状态列表的形式表示。

image.png

  • History对象的length属性表示浏览历史列表中的元素数量
  • History对象的back()和forward()方法与浏览器的“后退”和“前进”按钮一样:它们使浏览器在浏览历史中前后跳转一格。
  • go()接受一个整数参数,可以在历史列表中向前(正参数)或向后(负参数)跳过任意多个页
history.go(-2); //后退两个历史记录,相当于单击“后退”按钮两次

如果窗口包含多个子窗口(比如<iframe>元素),子窗口的浏览历史会按时间顺序穿插在主窗口的历史中。这意味着在主窗口调用history.back()(举例)可能会导致其中一个子窗口往回跳转到前一个显示的文档,但主窗口保留当前状态不变。

4. 浏览器信息Navigator和屏幕信息Screen

Window对象的navigator和screen属性。它们分别引用的是Navigator和Screen对象,可以获取Web浏览器或浏览器所在的桌面相关的信息

4.1 Navigator

Window对象的navigator属性引用的是包含浏览器厂商和版本信息的Navigator对象。

image.png

  • appName,Web浏览器的全称。在IE中,这就是"Microsoft Internet Explorer"。在Firefox中,该属性就是"Netscape"。为了兼容现存的浏览器嗅探代码,其他浏览器通常也取值为"Netscape"。
  • appVersion,此属性通常以数字开始,并跟着包含浏览器厂商和版本信息的详细字符串。字符串前面的数字通常是4.0或5.0,表示它是第4或第5代兼容的浏览器。
  • userAgent.浏览器在它的USER-AGENTHTTP头部中发送的字符串。这个属性通常包含appVersion中的所有信息,并且常常也可能包含其他的细节。由于这个属性包含绝大部分信息,因此浏览器嗅探代码通常用它来嗅探。

image.png

//为客户端嗅探定义browser.name和browser.version,这里使用了jQuery 1.4.1中的代码
//name和number都是字符串,对于不同的浏览器输出的结果也是不一样的,检测结果如下:
//
//"webkit":Safari或Chrome;版本号是Webkit的版本号
//"opera":Opera;版本号就是软件的版本号
//"mozilla":Firefox或者其他基于gecko内核的浏览器;版本号是Gecko的版本
//"msie":IE;版本号就是软件的版本
//
//比如Firefox 3.6返回:{name:"mozilla",version:"1.9.2"}
var browser = (function() {
  var s = navigator.userAgent.toLowerCase();
  var match =
    /(webkit)[\/]([\w.]+)/.exec(s) ||
    /(opera)(?:.*version)?[\/]([\w.]+)/.exec(s) ||
    /(msie)([\w.]+)/.exec(s) ||
    (!/compatible/.test(s) && /(mozilla)(?:.*?rv:([\w.]+))?/.exec(s)) ||
    [];

  return { name: match[1] || "", version: match[2] || "0" };
})();

image.png

  • platform.在其上运行浏览器的操作系统(并且可能是硬件)的字符串。
  • onLine.navigator.onLine属性(如果存在的话)表示浏览器当前是否连接到网络。

image.png

  • geolocation.Geolocation对象定义用于确定用户地理位置信息的接口

image.png

  • javaEnabled().一个非标准的方法,当浏览器可以运行Java小程序时返回true
  • cookieEnable()如果浏览器可以保存永久的cookie时,返回true。

image.png

4.2 Screen对象

Screen对象。它提供有关窗口显示的大小和可用的颜色数量的信息。

image.png

  • 属性width和height指定的是以像素为单位的窗口大小。
  • 属性availWidth和availHeight指定的是实际可用的显示大小,它们排除了像桌面任务栏这样的特性所占用的空间。
  • 属性colorDepth指定的是显示的BPP(bits-per-pixel)值,典型的值有16、24和32。(像素深度BPP是指存储每个像素所用的位数,也用它来度量图像的分辨率。例如,一幅彩色图像的每个像素用R,G,B三个分量表示,若每个分量用8位,那么一个像素共用24位表示,就说像素的深度为24,每个像素可以是16 777 216(2的24次方)种颜色中的一种。)

5. window对象的对话框

Window对象提供了3个方法来向用户显示简单的对话框。

  • alert()向用户显示一条消息并等待用户关闭对话框。
  • confirm()也显示一条消息,要求用户单击“确定”或“取消”按钮,并返回一个布尔值
  • prompt()同样也显示一条消息,等待用户输入字符串,并返回那个字符串
do {
  var name = prompt("What is your name?"); //得到一个字符串输入
  var correct = confirm(
    "You entered'" +
    name +
    "'.\n" + //得到一个布尔值
      "Click Okay to proceed or Cancel to re-enter."
  );
} while (!correct)

alert("Hello," + name); //输出一个纯文本消息

image.png

注意,这些对话框中显示的文本是纯文本,而不是HTML格式的文本。只能使用空格、换行符和各种标点符号来格式化这些对话框。

6. 多窗口和窗体

  • 一个Web浏览器窗口可能在桌面上包含多个标签页。每一个标签页都是独立的“浏览上下文”(browsing context),每一个上下文都有独立的Window对象,而且相互之间互不干扰。

  • 每个标签页中运行的脚本通常并不知道其他标签页的存在,更不用说和其他标签页的Window对象进行交互操作或者操作其文档内容了

  • 使用标签页,每个桌面窗口中的Window对象都是独立的,也就是说彼此就是完全独立的,和其他桌面窗口没有任何联系。

  • 但是窗口并不总是和其他窗口完全没关系。一个窗口或标签页中的脚本可以打开新的窗口或标签页,当一个脚本这样做时,这样多个窗口或窗口与另一个窗口的文档之间就可以互操作

HTML文档经常使用<iframe>来嵌套多个文档。由<iframe>所创建的嵌套浏览上下文是用它自己的Window对象所表示的。和相互独立的标签页不同,嵌套的浏览上下文之间并不是相互独立的。在一个窗体中运行的JavaScript程序总是可以看到它的祖先和子孙窗体

6.1 打开窗口window.open()

Window.open()载入指定的URL到新的或已存在的窗口中,并返回代表那个新窗口的Window对象。它有4个可选的参数。

  • 第一个参数是要在新窗口中显示的文档的URL。如果这个参数省略了(也可以是空字符串),那么会使用空页面的URL about:blank
  • 第二个参数是新打开的窗口的名字。如果指定的是一个已经存在的窗口的名字(并且脚本允许跳转到那个窗口),会直接使用已存在的窗口。否则,会打开新的窗口,并将这个指定的名字赋值给它。如果省略此参数,会使用指定的名字"_blank"打开一个新的、未命名的窗口。

如果其中一个窗口是内嵌在另一个窗口里的窗体,那么在它们的脚本之间就可以相互导航。这种情况下,可以使用保留的名字"_top"(顶级祖先窗口)和"_parent"(直接父级窗口)来获取彼此的浏览上下文。

窗口的名字,它允许open()方法引用已存在的窗口,并同时可以作为<a>和<form>元素上HTML target属性的值,用来表示引用的文档(或表单提交结果)应该显示在命名的窗口中。这个target属性的值可以设置为"_blank"、"_parent"或"_top",从而使引用的文档显示在新的空白窗口、父窗口/窗体或顶层窗口中。

Window对象如果有name属性,就用它保存名字。该属性是可写的,并且脚本可以随意设置它。如果传递给window.open()一个除"_blank"之外的名字,通过该调用创建的窗口将以该名字作为name属性的初始值。如果<iframe>元素有name属性,表示该iframe的Window对象会用它作为name属性的初始值。

image.png

  • open()的第三个可选参数是一个以逗号分隔的列表,如果指定这个参数,就可以指定窗口的尺寸,以及它包含的一组属性。

image.png

  • open()的第四个参数只在第二个参数命名的是一个存在的窗口时才有用。它是一个布尔值,声明了由第一个参数指定的URL是应用替换掉窗口浏览历史的当前条目(true),还是应该在窗口浏览历史中创建一个新的条目(false),后者是默认的设置。

  • open()的返回值是代表命名或新创建的窗口的Window对象

var w = window.open(); //打开一个新的空白窗口
w.alert("About to visit http://example.com"); //调用alert()方法
w.location = "http://example.com"; //设置它的location属性
  • 在由window.open()方法创建的窗口中,opener属性引用的是打开它的脚本的Window对象。在其他窗口中,opener为null:

image.png

image.png

  • 方法window.close()将关闭一个窗口。

6.2 窗体之间的关系

Window对象的方法open()返回代表新创建的窗口的Window对象。而且这个新窗口具有opener属性,该属性可以打开它的原始窗口。

  • 窗口或窗体中运行的代码可以引用到自己的窗口或窗体,以及嵌套的子窗体。
  • 任何窗口或窗体中的JavaScript代码都可以将自己的窗口和窗体引用为window或self。

image.png

  • 窗体可以用parent属性引用包含它的窗口或窗体的Window对象:

image.png

  • 无论一个窗体被嵌套了几层,它的top属性引用的都是指向包含它的顶级窗口。如果一个Window对象代表的是一个顶级窗口,那么它的top属性引用的就是窗口本身。

image.png

  • <iframe>元素有contentWindow属性,引用该窗体的Window对象:

image.png

  • 用表示窗体的Window对象的frameElement属性来获取该窗体的<iframe>元素。表示顶级窗口的Window对象的frameElement属性为null,iframe窗体中的Window对象的frameElement属性不是null

image.png

  • 每个Window对象都有一个frames属性,它引用自身包含的窗口或窗体的子窗体。frames属性引用的是类数组对象.注意frames[]数组里的元素是Window对象,而不是<iframe>元素。

image.png

6.3 交互窗口中的JavaScript

每个窗口和窗体都是它自身的JavaScript执行上下文,以Window作为全局对象。但是如果一个窗口或窗体中的代码可以应用到其他窗口或窗体(并且同源策略没有阻止它),那么一个窗口或窗体中的脚本就可以和其他窗口或窗体中的脚本进行交互。

  • 设想一个Web页面里有两个<iframe>元素,分别叫"A"和"B",并假设这些窗体所包含的文档来自于相同的一个服务器,并且包含交互脚本。
var i = 4; //窗体A里的脚本定义了一个变量i
  • 由于窗体B中的脚本可以引用窗体A的Window对象,因此它也可以引用那个Window对象的属性:
parent.A.i = 4; //改变窗体A中的变量i的值
  • 如果窗体B中的脚本声明了一个(非嵌套的)函数f,这个函数在窗体B中是全局变量,并且窗体B中的代码可以用f()调用f。但是窗体A中的代码必须将f作为窗体B的Window对象的f属性来引用:
parent.B.f();//调用窗体B中定义的一个函数
//如果窗体A中的代码需要很频繁地使用这个函数,则可以将这个函数赋值给窗体A中的一个变量
var f = parent.B.f;

当采用这种方式在窗体或窗口间共享函数时,函数在定义它的作用域中执行,而不是在调用它的作用域中执行。如果函数f引用了全局变量,那么将在窗体B的属性中查找这些变量,即使函数是由窗体A调用的。

  • 构造函数也是函数,所以当用构造函数和相关的原型对象定义一个类时,那个类只在一个单独的窗口中定义。每个窗体中的代码必须显式地用父级窗口的属性来引用Set()构造函数:
var s=new parent.Set();

和用户定义的类不同,内置的类(比如String,Date和RegExp)都会在所有的窗口中自动预定义。但是要注意,每个窗口都有构造函数的一个独立副本和构造函数对应原型对象的一个独立副本。

事实上,每个Window都有自己的原型对象,这意味着instanceof操作符不能跨窗口工作。