《JavaScript 高级程序设计》第10-16章

10.浏览器对象模型(BOM)

window 对象

BOM 的核心是 window 对象,表示浏览器的实例。

location 对象

location 对象是最有用的 BOM 对象之一,提供当前窗口中加载文档的信息及常用导航功能。它既是 window 属性又是 document 属性。

navigator 对象

navigator 对象的属性通常用于确定浏览器的类型。

screen 对象

screen 对象保存的纯粹是客户端能力信息,也就是浏览器窗口外面的客户端显示器信息

history 对象

history 对象表示当前窗口首次使用以来用户的导航历史记录,该对象并不会暴露用户访问过的 URL,但可以在不知道实际 URL 的情况下实现前进和后退。

导航

使用 go 方法可以在用户历史记录中沿任何方向导航,接收一个参数表示前进或后退多少步。可以使用 back 和 forward 方法简写,模拟浏览器的前进后退按钮。history 对象还有一个 length 属性表示历史记录中有多少条目。

历史状态管理

history.pushState 方法可以让开发者改变浏览器 URL 而不会加载新页面,该方法接收3个参数:一个 state 对象、一个新状态的标题和一个(可选)相对 URL。

let stateObject = {foo:'bar'}
history.pushState(stateObject, 'My Title', 'baz.html')

pushState 方法执行后,状态信息会被推到历史记录中,浏览器地址也会改变以反映新的相对 URL。虽然 location.href 返回的是地址栏中的新内容但是浏览器页不会向服务器发送请求。 可以通过 history.state 获取当前状态对象,使用 replaceState 覆盖当前状态。

11.文档对象模型(DOM)

节点层级

任何 HTML 和 XML 文档都可以用 DOM 表示为一个由节点组成的层级结构

Node 类型

Document 类型

Element 类型

Text 类型

12.DOM 扩展

Selectors API

元素遍历

DOM元素新增的5个属性:

HTML5

13.事件

事件流

事件流描述了页面接收时间的顺序。 事件冒泡的事件被定义为从最具体的元素开始触发,然后向上传播到没那么具体的元素。现代浏览器中的事件会一直冒泡到 windows 对象。 事件捕获的意思是最不具体的元素最先收到事件,最具体的节点最后收到事件。事件捕获是为了在事件到达最终目标前拦截事件。 DOM 事件流规定事件流分为3个阶段:事件捕获、到达目标和事件冒泡。

事件处理程序

为响应事件而调用的函数称为事件处理程序(或事件监听器)。 JavaScript 中传统的事件处理程序是把一个函数赋值给元素的一个事件处理程序属性。例如:

let btn = document.getElementById('myBtn');
btn.onClick = function() {
  console.log('Clicked');
};

DOM2 Events 为事件处理程序的赋值和移除定义了两个方法:addEventListener 和 removeEventListener。方法接收3个参数:事件名、事件处理函数和一个布尔值(true 表示在捕获阶段调用事件处理程序,false 表示在冒泡阶段调用事件处理程序)。使用 DOM2 方式的主要优势是可以为同一个事件添加多个事件处理程序。

事件对象

在 DOM 中发生事件时,所有相关信息会存储在 event 对象中,event 对象也是传给事件处理程序的唯一参数。

事件类型

用户界面事件

焦点事件

焦点事件在页面元素获得或失去焦点时触发。常用焦点事件包括:

鼠标和滚轮事件

键盘输入事件

HTML5 事件

内存与性能

模拟事件

14.错误处理与调试

15.JSON

16.网络请求与远程资源

XMLHttpRequest 对象

XHR 对象通过 XMLHttpRequest 构造函数创建,使用 open 方法配置并使用 send 方法发送。open 方法接收3个参数:请求类型,URL 以及 请求是否异步的布尔值。send 方法接收一个参数,作为请求体发送的数据。如果不需要请求体则必须传 null。

let xhr = new XMLHttpRequest()
xhr.open('get', 'example.php', false)
xhr.send(null)

收到服务器响应后,XHR 对象的以下属性会被填充数据:

此外 XHR 对象有一个 readyState 属性,表示当前处在请求/响应过程的哪个阶段。常用的值为 4,表示收到所有响应。readyState 改变时会触发 readystatechange 事件,可以为 XHR 对象添加事件处理程序。

let xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
  if (xhr.readyState == 4) {
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
      alert(xhr.responseText)
    } else {
      alert('request was unsuccessful:' + xhr.status)
    }
  }
}
xhr.open('get', 'example.txt', true)
xhr.send(null)

可以调用 abort 方法在收到响应前取消异步请求。 XHR 对象可以使用 setRequestHeader 方法设置额外的请求头,使用 getResponseHeader 方法传入想要获取的头部名称获取响应头,使用 getAllResponseHeaders 方法获取所有响应头的字符串。 FormData 类型用于表单序列化,也便于创建与表单类似格式的数据通过 XHR 对象发送。

进度事件

跨源资源共享

跨源资源共享(CORS)定义了浏览器与服务器如何实现跨源通信。其基本思路是使用自定义 HTTP 头部允许浏览器和服务器相互了解以确认请求或响应应该成功还是失败。 对于简单的请求,比如:GET 或 POST 类型,没有自定义头部且请求体是 text/plain 类型,这样的请求会在发送时有一个额外的头部 Origin,包含发送请求的页面的源(协议、域名和端口)。如果服务器决定响应请求,应该发送 Access-Control-Allow-Origin 头部包含相同的源或 *(表示资源公开)。如果没有这个头部或源不匹配则表明不会响应浏览器请求。无论请求还是响应都不会包含 cookie 信息。 对于上述简单请求外的情况,CORS 通过一种叫预检请求的服务器验证机制实现跨源。在发送自定义头部、GET 和 POST 外的方法以及不同请求体内容类型时,会先向服务器发送一个“预检”请求,这个请求使用 OPTIONS 方法发送并包含以下头部:

服务器在这个请求发送后可以确定是否允许这种类型的请求并在响应中发送如下头部:

预检请求返回后,结果会按照响应指定时间缓存一段时间。 默认情况下,跨源请求不提供凭据(cookie、HTTP 认证和客户端 SSL 证书)。可以通过 WithCredentials 属性设置为 true 来表明请求会发送凭据。如果服务器允许带凭据的请求,可以在响应中包含头部:Access-Control-Allow-Credentials: true。

替代性跨源技术

Fetch API

Fetch API 能够执行 XMLHttpRequest 对象的所有任务,但更易使用且接口更现代化,并且能在 Web 工作线程等现代 Web 工具中使用。

基本用法

fetch 方法只有一个必须的参数 input。多数情况下这个参数是要获取资源的 URL。这个方法返回一个期约:

let r = fetch('/bar')
  .then((response) => {
    console.log(response)
  })

读取 response 最简单的方式是取得文本格式的内容,使用 text 方法。 Fetch API 支持通过 Response 的 status 和 statusText 属性检查响应状态。跟随重定向时,响应对象的 redirected 属性会被设置为 true,状态码仍然是 200。 只使用 URL 时,fetch 方法会发送 GET 请求,只包含最低限度的请求头。进一步配置请求可以传入第二个参数 init 对象。init 对象的可选配置包括:

let abortController = new AbortController()

fetch('wikipedia.zip', {signal: abortController.signal})
  .catch(() => console.log('aborted!'))

// 10毫秒后中断请求
setTimeout(() => abortController.abort(), 10)

Headers 对象

Headers 对象是所有外发请求和入站响应头部的容器。Headers 对象与 Map 对象极为相似,都有 get、set、has 和 delete 等实例方法。此外,在初始化 Headers 对象的时候也可以使用键值对的形式。 Headers 对象通过 append 方法添加多个值。在 Headers 实例中还不存在的头部上调用 append 方法相当于调用 set,后续调用会以逗号为分隔符拼接多个值。

let h = new Headers()

h.append('foo', 'bar')
console.log(h.get('foo')) // 'bar'

h.append('foo', 'baz')
console.log(h.get('foo')) //'bar, baz'

Request 对象

Request 对象是获取资源请求的接口,暴露请求相关信息和请求体的不同方式。 Request 构造函数接收 input 参数和 init 对象,其中 input 参数一般为 URL,init 参数与 fetch 方法一样。 Fetch API 提供了两种创建 Request 对象副本的方式:

  1. 使用 Request 构造函数
let r1 = new Request('https://foo.com')
let r2 = new Request(r1)

console.log(r2.url) // https://foo.com

如果传入 init 对象则会覆盖源对象中同名的值

let r1 = new Request('https://foo.com')
let r2 = new Request(r1, {method: 'post'})

console.log(r1.method) // GET
console.log(r2.method) // POST

这种克隆方式并不总能得到一模一样的副本。第一个请求的请求体会被标记为“已使用”(bodyUsed 属性为 true)。如果源对象与创建的新对象不同源,则 referrer 属性会被清除。如果源对象的 mode 为navigate,则会被转换为 same-origin。

  1. 使用 clone 方法可以创建一模一样的副本,并且请求体不会被标记为“已使用”
let r1 = new Request('https://foo.com', {method: 'POST', body: 'foobar'})
let r2 = r1.clone()

console.log(r1.url) // https://foo.com
console.log(r2.url) // https://foo.com

console.log(r1.bodyUsed) // false
console.log(r2.bodyUsed) // false

如果请求对象的 bodyUsed 属性为 true,上述任何一种方式都不能用来创建这个对象的副本。 fetch 方法可以直接传入创建好的 Request 实例。

Response 对象

大多数情况下,产生 Response 对象的主要方式是调用 fetch 方法,它会返回一个最后会解决为 Response 对象的期约。 Response 类还有两个生成 Response 对象的静态方法:

  1. Response.redirect:接收一个 URL 和一个重定向状态码并返回重定向的 Response 对象
  2. Response.error:产生表示网络错误的 Response 对象

Response 对象也有 clone 方法,用法与 Request 对象类似。

Request、Response 及 Body 混入

Request 和 Response 都使用了 Fetch API 的 Body 混入,为上述两种类型提供了只读的 body 属性(实现为 ReadableStream)、只读的 bodyUsed 布尔值(表示 body 流是否已读)和一组方法。 Body 混入提供了5个方法,用于将 ReadableStream 转存到缓存区的内存里,将缓冲区转换为某种 JavaScript 对象类型,以及通过期约来产生结果。

  1. Body.text 方法返回期约,解决为 UTF-8 格式字符串
  2. Body.json 方法返回期约,解决为 JSON
  3. Body.formData 方法返回期约,解决为 FormData 实例
  4. Body.arrayBuffer 方法返回期约,解决为 ArrayBuffer 实例
  5. Body.blob 方法返回期约,解决为 Blob 实例

Web Socket

Web Socket(套接字)的目标是通过一个长时连接实现与服务器全双工、双向的通信。因为 Web Socket 使用了自定义协议,所以 URL 方案(scheme)要使用 ws:// 和 wss://。 创建一个新的 Web Socket 需要实例化一个 WebSocket 对象并传入提供连接的 URL:

let socket = new WebSocket('ws://www.example.com/server.php')

必须给 WebSocket 构造函数传入一个绝对 URL。同源策略不适用与 WebSocket。浏览器会在初始化 WebSocket 对象后立即创建连接。使用 close 方法可以关闭 Web Socket 连接。 使用 send 方法向服务器发送数据,包括字符串、ArrayBuffer 或 Blob:

let socket = new WebSocket('ws://www.example.com/server.php')

let stringData = 'Hello World!'
let arrayBufferData = Unit8Array.from(['f', 'o', 'o'])
let blobData = new Blob(['f', 'o', 'o'])

socket.send(stringData)
socket.send(arrayBufferData.buffer)
socket.send(blobData)

服务器向客户端发送消息时,WebSocket 对象上会触发 message 事件。可以通过事件处理函数的 event.data 属性访问到有效载荷。返回的数据也可能是 blob 或 arraybuffer。

socket.onmessage = function (event) {
  let data = event.data
}

WebSocket 对象在连接生命周期中还有可能触发 3 个其他事件:

  1. open:连接成功建立时触发
  2. error:发生错误时触发
  3. close:连接关闭时触发
相关文章