reset - Web API 接口参考

reset
=====

当表单被重置时触发`reset`事件。

综合信息
----

Specification

HTML5

Interface
`Event`
Bubbles

是（可以指定不发生冒泡的简单事件）

Cancelable

是

Target

Element

默认行为

重置父表单元素所有的值。

属性
--

| Property | Type | Description |
| --- | --- | --- |
| `target` 只读  | `EventTarget` | 事件的目标（DOM 树最顶端的元素）。 |
| `type` 只读  | `DOMString` | 事件的类型。 |
| `bubbles` 只读  | `Boolean` | 是否指定事件冒泡。 |
| `cancelable` 只读  | `Boolean` | 事件是否可以被取消。 |

规范
--

| Specification |
| --- |
| HTML Standard # event-reset |


form.reset - Web API 接口参考

form.reset
==========

### 概述

`reset` 方法可以重置一个表单内的所有表单控件的值到初始状态。

### 语法
```

HTMLFormElement.reset()
```

### 例子
```

document.forms["myform"].reset();
```

### 备注

运行该方法和点击表单的重置按钮是一样的效果。

### 规范

DOM Level 2 HTML: reset


MediaDevices.getDisplayMedia() - Web API 接口参考

MediaDevices.getDisplayMedia()
==============================

这个 `MediaDevices` 接口的 **`getDisplayMedia()`** 方法提示用户去选择和授权捕获展示的内容或部分内容（如一个窗口）在一个`MediaStream` 里。然后，这个媒体流可以通过使用 MediaStream Recording API 被记录或者作为WebRTC 会话的一部分被传输。

去 Using the Screen Capture API (en-US) 查找更多详情和例子。

语法
--
```

var promise = navigator.mediaDevices.getDisplayMedia(constraints);
```

### 参数

`constraints` 可选

一个可选的`MediaStreamConstraints` (en-US)对象，它指定了返回的`MediaStream`的要求。因为`getDisplayMedia()`需要视频轨道，所以即使`constraints` 对象没有明确请求视频轨道，返回的流也会有一个。

### 返回值

一个被解析为 `MediaStream` 的 `Promise`，其中包含一个视频轨道。视频轨道的内容来自用户选择的屏幕区域以及一个可选的音频轨道。

**备注：** 浏览器对音频的支持程度各不相同，既取决于是否支持，也取决于音频源。点击 浏览器兼容性 来查看各个浏览器的支持性。

### 异常

来自返回的 promise 的拒绝是通过将`DOMException`错误对象传递给 promise 的失败处理程序来进行的。可能的错误是：

`AbortError` ［中止错误］

发生了与以下任何其他异常不匹配的错误或故障。

`InvalidStateError` ［拒绝错误］

调用 `getDisplayMedia()` 的 context 中的 `document` 不是完全激活的; 例如，也许它不是最前面的标签。

`NotAllowedError` ［拒绝错误］

用户拒绝授予访问屏幕区域的权限，或者不允许当前浏览实例访问屏幕共享。

`NotFoundError` ［找不到错误］

没有可用于捕获的屏幕视频源。

`NotReadableError` ［无法读取错误］

用户选择了屏幕，窗口，标签或其他屏幕数据源，但发生了硬件或操作系统级别错误或锁定，从而预先占用了共享所选源。

`OverconstrainedError` ［转换错误］

创建流后，由于无法生成兼容的流导致应用指定的 `constraints` 失效。

`TypeError` ［类型错误］

指定的 `constraints` 包括调用 `getDisplayMedia()` 时不允许的`constraints`。这些不受支持的`constraints`是 `advanced` 的，任何约束又有一个名为 `min` 或 `exact` 的成员。

示例
--

在下面的示例中，我们创建了一个 `startCapture()` 方法，该方法在给定由 `displayMediaOptions` 参数指定的一组选项的情况下启动屏幕捕获。选项以 `MediaStreamConstraints` (en-US)对象的形式指定，该对象指定首选流配置和从中捕获视频的显示表面

```js
async function startCapture(displayMediaOptions) {
  let captureStream = null;

  try {
    captureStream =
      await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
  } catch (err) {
    console.error("Error: " + err);
  }
  return captureStream;
}
```

这里使用 `await` 来进行异步等待 `getDisplayMedia()`来进行 `MediaStream`解析，其中包含指定选项所请求的显示内容。之后，流被返回给调用者以供其使用，可能是使用 `RTCPeerConnection.addTrack()`添加到 WebRTC 调用来从流中添加视频轨道。

规范
--

| Specification |
| --- |
| Screen Capture # dom-mediadevices-getdisplaymedia |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参考
--

* Screen Capture API
* Using the Screen Capture API (en-US)
* 媒体捕捉与媒体流 API
* WebRTC API
* `getUserMedia()`: Capturing media from a camera and/or microphone


MediaDevices.enumerateDevices() - Web API 接口参考

MediaDevices.enumerateDevices()
===============================

`MediaDevices` 的方法 **`enumerateDevices()`** 请求一个可用的媒体输入和输出设备的列表，例如麦克风，摄像机，耳机设备等。返回的 `Promise` (en-US) 完成时，会带有一个描述设备的 `MediaDeviceInfo` (en-US) 的数组。

语法
--
```

var enumeratorPromise = navigator.mediaDevices.enumerateDevices();
```

### 返回值

返回一个 `Promise` (en-US) 。当完成时，它接收一个 `MediaDeviceInfo` (en-US) 对象的数组。每个对象描述一个可用的媒体输入输出设备。.

如果枚举失败，promise 也会被拒绝（rejected）。

示例
--

这是一个使用 `enumerateDevices()` 的例子。它只是输出一个带有标签（有标签的情况下）的 device ID (en-US) 的列表。

```js
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
  console.log("不支持 enumerateDevices() .");
  return;
}

// 列出相机和麦克风。

navigator.mediaDevices
  .enumerateDevices()
  .then(function (devices) {
    devices.forEach(function (device) {
      console.log(
        device.kind + ": " + device.label + " id = " + device.deviceId,
      );
    });
  })
  .catch(function (err) {
    console.log(err.name + ": " + err.message);
  });
```

这将会输出类似以下的内容：
```

videoinput: id = csO9c0YpAf274OuCPUA53CNE0YHlIr2yXCi+SqfBZZ8=
audioinput: id = RKxXByjnabbADGQNNZqLVLdmXlS0YkETYCIbg+XxnvM=
audioinput: id = r2/xw1xUPIyZunfV1lGrKOma5wTOvCkWfZ368XCndm0=
```

或者，如果有一个或多个 `MediaStream` 处于活动状态或者获得了持久授权，将会输出以下内容：
```

videoinput: FaceTime HD Camera (Built-in) id=csO9c0YpAf274OuCPUA53CNE0YHlIr2yXCi+SqfBZZ8=
audioinput: default (Built-in Microphone) id=RKxXByjnabbADGQNNZqLVLdmXlS0YkETYCIbg+XxnvM=
audioinput: Built-in Microphone id=r2/xw1xUPIyZunfV1lGrKOma5wTOvCkWfZ368XCndm0=
```

规范
--

| Specification |
| --- |
| Media Capture and Streams # dom-mediadevices-enumeratedevices |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `navigator.mediaDevices.getUserMedia()` (en-US)
* WebRTC (en-US) - the introductory page to the API
* MediaStream API (en-US) - the API for the media stream objects
* Taking webcam photos (en-US) - a tutorial on using `getUserMedia()` for taking photos rather than video.


MediaDevices.getSupportedConstraints() - Web API 接口参考

MediaDevices.getSupportedConstraints()
======================================

`MediaDevices` 接口的 **`getSupportedConstraints()`** 方法返回一个基于 `MediaTrackSupportedConstraints` (en-US) 的对象，其成员字段都是客户端（user agent）所支持的约束属性（如帧率，窗口大小）。

语法
--
```

var supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
```

### 参数

无

### 返回值

一个新的基于`MediaTrackSupportedConstraints` (en-US) 的对象用来监视客户端所支持的约束属性。因为只有客户端所支持的约束属性才能被展示在这个列表中 , 这些布尔值（Boolean）属性的每一个都为 true。

示例
--

这个示例展示了你的客户端所支持的约束属性的列表。
```

<p>The following media constraints are supported by your browser:</p>

<ul id="constraintList"></ul>
```

```
body {
  font:
    15px Arial,
    sans-serif;
}
```

```js
let constraintList = document.getElementById("constraintList");
let supportedConstraints = navigator.mediaDevices.getSupportedConstraints();

for (let constraint in supportedConstraints) {
  if (supportedConstraints.hasOwnProperty(constraint)) {
    let elem = document.createElement("li");

    elem.innerHTML = "<code>" + constraint + "</code>";
    constraintList.appendChild(elem);
  }
}
```

### 结果

规范
--

| Specification |
| --- |
| Media Capture and Streams # dom-mediadevices-getsupportedconstraints |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


MediaDevices.getUserMedia() - Web API 接口参考

MediaDevices.getUserMedia()
===========================

**`MediaDevices.getUserMedia()`** 会提示用户给予使用媒体输入的许可，媒体输入会产生一个`MediaStream`，里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道（来自硬件或者虚拟视频源，比如相机、视频采集设备和屏幕共享服务等等）、一个音频轨道（同样来自硬件或虚拟音频源，比如麦克风、A/D 转换器等等），也可能是其他轨道类型。

它返回一个 `Promise` 对象，成功后会`resolve`回调一个 `MediaStream` 对象。若用户拒绝了使用权限，或者需要的媒体源不可用，`promise`会`reject`回调一个 `PermissionDeniedError` 或者 `NotFoundError` 。

**备注：** 返回的 promise 对象可能既不会 resolve 也不会 reject，因为用户不是必须选择允许或拒绝。

通常你可以使用 `navigator.mediaDevices` 来获取 `MediaDevices` ，例如：

```js
navigator.mediaDevices
  .getUserMedia(constraints)
  .then(function (stream) {
    /\* 使用这个 stream stream \*/
  })
  .catch(function (err) {
    /\* 处理 error \*/
  });
```

语法
--

```js
var promise = navigator.mediaDevices.getUserMedia(constraints);
```

### 参数

`constraints`

作为一个`MediaStreamConstraints` (en-US) 对象，指定了请求的媒体类型和相对应的参数。

constraints 参数是一个包含了`video` 和 `audio`两个成员的`MediaStreamConstraints` 对象，用于说明请求的媒体类型。必须至少一个类型或者两个同时可以被指定。如果浏览器无法找到指定的媒体类型或者无法满足相对应的参数要求，那么返回的 Promise 对象就会处于 rejected［失败］状态，`NotFoundError`作为 rejected［失败］回调的参数。

以下同时请求不带任何参数的音频和视频：

```js
{ audio: true, video: true }
```

如果为某种媒体类型设置了 `true` ，得到的结果的流中就需要有此种类型的轨道。如果其中一个由于某种原因无法获得，`getUserMedia()` 将会产生一个错误。

当由于隐私保护的原因，无法访问用户的摄像头和麦克风信息时，应用可以使用额外的 constraints 参数请求它所需要或者想要的摄像头和麦克风能力。下面演示了应用想要使用 1280x720 的摄像头分辨率：

```js
{
  audio: true,
  video: { width: 1280, height: 720 }
}
```

浏览器会试着满足这个请求参数，但是如果无法准确满足此请求中参数要求或者用户选择覆盖了请求中的参数时，有可能返回其他的分辨率。

强制要求获取特定的尺寸时，可以使用关键字`min`、`max` 或者 `exact`（就是 min == max）。以下参数表示要求获取最低为 1280x720 的分辨率。

```js
{
  audio: true,
  video: {
    width: { min: 1280 },
    height: { min: 720 }
  }
}
```

如果摄像头不支持请求的或者更高的分辨率，返回的 Promise 会处于 rejected 状态，`NotFoundError 作为`rejected 回调的参数，而且用户将不会得到要求授权的提示。

造成不同表现的原因是，相对于简单的请求值和`ideal`关键字而言，关键字`min`, `max`, 和 `exact`有着内在关联的强制性，请看一个更详细的例子：

```js
{
  audio: true,
  video: {
    width: { min: 1024, ideal: 1280, max: 1920 },
    height: { min: 776, ideal: 720, max: 1080 }
  }
}
```

当请求包含一个 ideal（应用最理想的）值时，这个值有着更高的权重，意味着浏览器会先尝试找到最接近指定的理想值的设定或者摄像头（如果设备拥有不止一个摄像头）。

简单的请求值也可以理解为是应用理想的值，因此我们的第一个指定分辨率的请求也可以写成如下：

```js
{
  audio: true,
  video: {
    width: { ideal: 1280 },
    height: { ideal: 720 }
  }
}
```

并不是所有的 constraints 都是数字。例如，在移动设备上面，如下的例子表示优先使用前置摄像头（如果有的话）：

```js
{ audio: true, video: { facingMode: "user" } }
```

强制使用后置摄像头，请用：

```js
{ audio: true, video: { facingMode: { exact: "environment" } } }
```

### 返回值

返回一个 `Promise`，这个 Promise 成功后的回调函数带一个 `MediaStream` 对象作为其参数。

### 异常

返回一个失败状态的 Promise，这个 Promise 失败后的回调函数带一个`DOMException`对象作为其参数。可能的异常有：

`AbortError`［中止错误］

尽管用户和操作系统都授予了访问设备硬件的权利，而且未出现可能抛出`NotReadableError`异常的硬件问题，但仍然有一些问题的出现导致了设备无法被使用。

`NotAllowedError`［拒绝错误］

用户拒绝了当前的浏览器实例的访问请求；或者用户拒绝了当前会话的访问；或者用户在全局范围内拒绝了所有媒体访问请求。

**备注：** 较旧版本的规范使用了`SecurityError`，但在新版本当中`SecurityError`被赋予了新的意义。

`NotFoundError`［找不到错误］

找不到满足请求参数的媒体类型。

`NotReadableError`［无法读取错误］

尽管用户已经授权使用相应的设备，操作系统上某个硬件、浏览器或者网页层面发生的错误导致设备无法被访问。

`OverconstrainedError`［无法满足要求错误］

指定的要求无法被设备满足，此异常是一个类型为`OverconstrainedError`的对象，拥有一个`constraint`属性，这个属性包含了当前无法被满足的`constraint`对象，还拥有一个`message`属性，包含了阅读友好的字符串用来说明情况。

**备注：** 因为这个异常甚至可以在用户尚未授权使用当前设备的情况下抛出，所以应当可以当作一个探测设备能力属性的手段［fingerprinting surface］。

`SecurityError`［安全错误］

在`getUserMedia()` 被调用的 `Document` 上面，使用设备媒体被禁止。这个机制是否开启或者关闭取决于单个用户的偏好设置。

`TypeError`［类型错误］

constraints 对象未设置［空］，或者都被设置为`false`。

示例
--

### 宽度和高度

这个例子设置了摄像头分辨率，并把结果的 `MediaStream` 分配给了一个 video 元素。

```js
// 想要获取一个最接近 1280x720 的相机分辨率
var constraints = { audio: true, video: { width: 1280, height: 720 } };

navigator.mediaDevices
  .getUserMedia(constraints)
  .then(function (mediaStream) {
    var video = document.querySelector("video");
    video.srcObject = mediaStream;
    video.onloadedmetadata = function (e) {
      video.play();
    };
  })
  .catch(function (err) {
    console.log(err.name + ": " + err.message);
  }); // 总是在最后检查错误
```

### 在旧的浏览器中使用新的 API

这是一个使用 `navigator.mediaDevices.getUserMedia()`的例子，带一个 polyfill 以适应旧的浏览器。要注意的是这个 polyfill 并不能修正一些约束语法上的遗留差异，这表示约束在某些浏览器上可能不会很好地运行。推荐使用处理了约束的 adapter.js polyfill 来替代。

```js
// 老的浏览器可能根本没有实现 mediaDevices，所以我们可以先设置一个空的对象
if (navigator.mediaDevices === undefined) {
  navigator.mediaDevices = {};
}

// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
// 因为这样可能会覆盖已有的属性。这里我们只会在没有 getUserMedia 属性的时候添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {
  navigator.mediaDevices.getUserMedia = function (constraints) {
    // 首先，如果有 getUserMedia 的话，就获得它
    var getUserMedia =
      navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

    // 一些浏览器根本没实现它 - 那么就返回一个 error 到 promise 的 reject 来保持一个统一的接口
    if (!getUserMedia) {
      return Promise.reject(
        new Error("getUserMedia is not implemented in this browser"),
      );
    }

    // 否则，为老的 navigator.getUserMedia 方法包裹一个 Promise
    return new Promise(function (resolve, reject) {
      getUserMedia.call(navigator, constraints, resolve, reject);
    });
  };
}

navigator.mediaDevices
  .getUserMedia({ audio: true, video: true })
  .then(function (stream) {
    var video = document.querySelector("video");
    // 旧的浏览器可能没有 srcObject
    if ("srcObject" in video) {
      video.srcObject = stream;
    } else {
      // 防止在新的浏览器里使用它，应为它已经不再支持了
      video.src = window.URL.createObjectURL(stream);
    }
    video.onloadedmetadata = function (e) {
      video.play();
    };
  })
  .catch(function (err) {
    console.log(err.name + ": " + err.message);
  });
```

### 帧率

在某些情况下，比如 WebRTC 上使用受限带宽传输时，低帧率可能更适宜。

```js
var constraints = { video: { frameRate: { ideal: 10, max: 15 } } };
```

### 前置或者后置摄像头

在移动设备（电话）上

```js
var front = false;
document.getElementById("flip-button").onclick = function () {
  front = !front;
};

var constraints = { video: { facingMode: front ? "user" : "environment" } };
```

权限
--

在一个可安装的 app（如Firefox OS app）中使用 `getUserMedia()` ，你需要在声明文件中指定以下的权限：

```js
"permissions": {
  "audio-capture": {
    "description": "Required to capture audio using getUserMedia()"
  },
  "video-capture": {
    "description": "Required to capture video using getUserMedia()"
  }
}
```

参见 permission: audio-capture 和 permission: video-capture 来获取更多信息。

规范
--

| Specification |
| --- |
| Media Capture and Streams # dom-mediadevices-getusermedia |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参考
--

* 旧的 navigator.getUserMedia 遗留 API
* navigator.enumerateDevices - 获取用户可用的设备类型和数量
* WebRTC (en-US) - WebRTC API 的介绍页面
* MediaStream API (en-US) - 媒体流对象的 API
* Taking webcam photos (en-US) - 一个关于使用 `getUserMedia()` 拍照而非录视频的教程


devicechange - Web API 接口参考

devicechange
============

每当媒体设备（例如相机，麦克风或扬声器）连接到系统或从系统中移除时，`devicechange` 事件就会被发送到 `MediaDevices` 实例。这是一个没有附加属性的通用 `Event` 。

一般信息
----

接口

Event

是否冒泡

No

是否可取消

No

对象
`MediaDevices`
默认动作

None

属性
--

| Property | Type | Description |
| --- | --- | --- |
| `target`只读  | `EventTarget` | 事件对象 (位于 DOM 树最上面的元素). |
| `type` 只读  | `DOMString` | 事件类型。 |
| `bubbles` 只读  | `Boolean` (en-US) | 是否冒泡 |
| `cancelable` 只读  | `Boolean` (en-US) | 是否可被取消 |

相关事件
----

*无*.

样例
--

TBD

规范
--

| Specification |
| --- |
| Media Capture and Streams # event-mediadevices-devicechange |
| Media Capture and Streams # dom-mediadevices-ondevicechange |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `MediaDevices.ondevicechange`


TextEncoder.encode() - Web API 接口参考

TextEncoder.encode()
====================

**`TextEncoder.encode()`** 方法接受一个字符串作为输入，返回一个对参数中给定的文本的编码后的 `Uint8Array`，编码的方法通过 `TextEncoder` 对象指定。

语法
--

```js
encode(string)
```

### 参数

`string`

一个包含了将要编码的文本。

### 返回值

一个 `Uint8Array` (en-US) 对象。

示例
--

```html
<p class="source">This is a sample paragraph.</p>
<p class="result">Encoded result:</p>
```

```js
const sourcePara = document.querySelector(".source");
const resultPara = document.querySelector(".result");
const string = sourcePara.textContent;

const textEncoder = new TextEncoder();

let encoded = textEncoder.encode(string);
resultPara.textContent += encoded;
```

规范
--

| Specification |
| --- |
| Encoding Standard # ref-for-dom-textencoder-encode① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 属于 `TextEncoder` 接口。


TextEncoder.encoding - Web API 接口参考

TextEncoder.encoding
====================

**`TextEncoder.encoding`** 只读属性返回一个字符串，其中包含特定编码器使用的编码算法的名称。

它只能具有以下值：`utf-8`。

值
-

值为 `utf-8` 的字符串。

规范
--

| Specification |
| --- |
| Encoding Standard # dom-textencoder-encoding |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 属于 `TextEncoder` 接口。


TextEncoder.encodeInto() - Web API 接口参考

TextEncoder.encodeInto()
========================

**`TextEncoder.encodeInto()`** 方法接受一个要编码的字符串和一个目标 `Uint8Array`，将生成的 UTF-8 编码的文本放入目标数组中，并返回一个指示编码进度的字典对象。这相比于旧的 `encode()` 方法性能更高——尤其是当目标缓冲区是 WASM 堆视图时。

语法
--

```js
encodeInto(string, uint8Array)
```

### 参数

`string`

一个字符串，包含将要编码的文本。

`uint8Array`

一个 `Uint8Array` 对象实例，用于将生成的 UTF-8 编码的文本放入其中。

### 返回值

一个对象，包含两个参数。

`read`

已经从源字符串中转换为 UTF-8 的，使用 UTF-16 编码的码元数。如果 `uint8Array` 没有足够的空间，则此值可能小于 `string.length`。

`written`

在目标 `Uint8Array` 中修改的字节数。写入的的字节确保形成完整的 UTF-8 字节序列。

编码到特定的位置
--------

*encoder*`.encodeInto()` 总是将它输出的结果放在数组的开头。然而，有时使输出从一个指定的索引开始很有用。解决方案是：`TypedArray.prototype.subarray()`：

```js
const encoder = new TextEncoder();

function encodeIntoAtPosition(string, u8array, position) {
  return encoder.encodeInto(
    string,
    position ? u8array.subarray(position | 0) : u8array,
  );
}

const u8array = new Uint8Array(8);
encodeIntoAtPosition("hello", u8array, 2);
console.log(u8array.join()); // 0,0,104,101,108,108,111,0
```

缓冲区大小
-----

转换一个 JavaScript 字符串 `s` 到 UTF-8 序列时，完全转换输出所需的空间从不会小于 `s.length` 个字节，也从不会大于 `s.length * 3` 字节。如果预计输出到缓冲区（通常是在 WASM 堆中）的生命周期很短，则可以为输出分配 `s.length * 3` 字节的空间，这样就能保证在第一次尝试中转换整个字符串。注意，`s.length * 3` 的情况是很少见的，因为很少有字符串中一定包含扩展之后为 3 个字节的字符。长文本的长度将不太可能超过 `s.length * 2` 个字节。因此，更乐观的方法可能是分配 `s.length * 2 + 5`个字节的空间，并且在极少数的情况下（乐观分配的方法出现错误）进行重新分配。

如果预计输出的生命周期很长，则可以计算最小分配空间为 `roundUpToBucketSize(s.length)`，最大分配空间为 `s.length * 3`，并且选择一个阈值 `t`（作为内存使用和速度之间的折衷方案），使得 `roundUpToBucketSize(s.length) + t >= s.length * 3` 时，分配 `s.length * 3` 的空间。否则，首先为 `roundUpToBucketSize(s.length)` 分配空间并进行转换。如果返回的字典对象中的 `read` 的大小为 `s.length`，则表示转换完成。如果不是，将目标的缓冲区的大小重新分配为 `written + (s.length - read) * 3`，并从字符串 `s` 中的索引 `read` 处开始取子字符串，并从目标缓冲区的索引 `written` 处开始取子缓冲区进行转换。

上述 `roundUpToBucketSize()` 函数，用于将空间向上舍入到分配器的桶大小。例如，如果你的 Wasm 分配器使用 2 的幂次方桶，则 `roundUpToBucketSize()` 应该返回该参数（如果它是 2 的幂次方的话，否则返回下一个 2 的幂次方）。如果 Wasm 分配器的行为未知，则 `roundUpToBucketSize()` 应为恒等函数。

如果你分配器的行为未知，但是你可能想要进行多达两次重新分配，并使得第一次重分配的步骤将*剩余未转换*的长度乘以 2 而不是 3。然而，在这种情况下，将*已经写入*缓冲区的长度乘以 2 通常是没有意义的，因为在这种情况下，如果发生第二次重分配，与原始长度乘 3 相比，它将总是过度分配。上述建议假设你不需要为 0 终止符分配空间。也就是说，在 WASM 方面，你正在使用 Rust 字符串或者一个不以 0 终止的 C++ 类。如果你正在使用 C++ `std::string` 标准库，即使逻辑长度已经向你展示，当计算向上舍入到分配器的桶大小的时候，你还是需要去考虑额外的终止符字节。请参阅下一节有关 C 字符串的内容。

没有 0 终止符
--------

如果输入字符串在输入中包含字符 U+0000，`encodeInto()` 将在输出中写入一个 0x00 字节。`encodeInto()` *不会*在逻辑输出后写入 C 风格的 0x00 标记字节。

如果你的 Wasm 程序使用 C 字符串，那么你有职责去写 `0x00` 的标记，并且如果你的 JavaScript 字符串包含 `U+0000`，则无法阻止 Wasm 程序看到逻辑截断的字符串。观察：

```js
const encoder = new TextEncoder();

function encodeIntoWithSentinel(string, u8array, position) {
  const stats = encoder.encodeInto(
    string,
    position ? u8array.subarray(position | 0) : u8array,
  );
  if (stats.written < u8array.length) u8array[stats.written] = 0; // append null if room
  return stats;
}
```

示例
--

```html
<p class="source">This is a sample paragraph.</p>
<p class="result"></p>
```

```js
const sourcePara = document.querySelector(".source");
const resultPara = document.querySelector(".result");
const string = sourcePara.textContent;

const textEncoder = new TextEncoder();
const utf8 = new Uint8Array(string.length);

const encodedResults = textEncoder.encodeInto(string, utf8);
resultPara.textContent +=
  `Bytes read: ${encodedResults.read}` +
  ` | Bytes written: ${encodedResults.written}` +
  ` | Encoded result: ${utf8}`;
```

规范
--

| Specification |
| --- |
| Encoding Standard # ref-for-dom-textencoder-encodeinto① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 属于 `TextEncoder` 接口
* `TextEncoder.encode()`


TextEncoder() - Web API 接口参考

TextEncoder()
=============

**`TextEncoder()`** 构造函数总是返回一个新创建的 `TextEncoder` 对象，该对象将生成具有 UTF-8 编码的字节流。

语法
--

```js
new TextEncoder();
```

### 参数

无。

规范
--

| Specification |
| --- |
| Encoding Standard # ref-for-dom-textencoder① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 属于 `TextEncoder` 接口。


PromiseRejectionEvent.promise - Web API 接口参考

PromiseRejectionEvent.promise
=============================

`PromiseRejectionEvent`事件对象的 **`promise`** 属性是只读的，表明 Promise 被 reject 的原因。你可以通过检查`PromiseRejectionEvent.reason` (en-US)来了解 Promise 为什么被 reject。

Syntax
------
```

promise = PromiseRejectionEvent.promise
```

### Value

一个被 reject 的，并且错误未被处理的`Promise`

Examples
--------

下面的例子监听了未被处理的 promise，如果`reason` (en-US) 是一个对象，并且其`code`属性包含了一段文本“Module not read.”，一个空闲的回调函数被声明，当任务执行错误时会进行重试

`event.preventDefault()` 用来表明该 promise 已被处理

```js
window.onunhandledrejection = function (event) {
  if (event.reason.code && event.reason.code == "Module not ready") {
    window.requestIdleCallback(function (deadline) {
      loadModule(event.reason.moduleName).then(performStartup);
    });
    event.preventDefault();
  }
};
```

Specifications
--------------

| Specification |
| --- |
| HTML Standard # dom-promiserejectionevent-promise |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `Promise`
* `PromiseRejectionEvent`
* `rejectionhandled`
* `unhandledrejection`


PromiseRejectionEvent.PromiseRejectionEvent() - Web API 接口参考

PromiseRejectionEvent.PromiseRejectionEvent()
=============================================

**`PromiseRejectionEvent()`** 构造器返回一个新创建的 `PromiseRejectionEvent`，代表一个 JavaScript `Promise`被 rejected 时触发的事件。

语法
--
```

new PromiseRejectionEvent(type, {
  promise: somePromise,
  reason : someValue
});
```

### 参数

*`PromiseRejectionEvent()` 构造函数继承了 `Event()`的参数。*

`type`

一个代表 PromiseRejectionEvent 的类型名称的字符串。这是区分大小写的同时必须是 `rejectionhandled` 或者 `unhandledrejection` 其中之一。

`promise`

代表被 rejected 的`Promise`。

`reason`

代表 promise 被 rejected 的原因的值或者对象`Object` 。

例子
--

```js
var myRejectionEvent = new PromiseRejectionEvent("unhandledrejection", {
  promise: myPromise,
  reason: "My house is on fire",
});
```

规范
--

| Specification |
| --- |
| HTML Standard # unhandled-promise-rejections:dom-event-constructor |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `Promise`
* `PromiseRejectionEvent`


CacheStorage.keys() - Web API 接口参考

CacheStorage.keys()
===================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

`CacheStorage` 接口的 **`keys()`** 方法返回一个 `Promise`对象，它使用一个数组 resolve，该数组包含 `CacheStorage` 对象按创建顺序跟踪的所有命名 `Cache` 对象对应的字符串。使用此方法迭代所有 `Cache` 对象。

语法
--
```

caches.keys().then(function(keyList) {
  //对 keyList 做操作
});
```

### 返回

一个使用 `CacheStorage` 对象中 `Cache` 名称数组 resolve 的 `Promise`

### 参数

无。

示例
--

在此代码片段中，我们监听`activate` 事件，然后运行一个 `waitUntil()` 方法，该方法在新的 service worker 被激活之前清除老的、无用的 cache。这里我们设置一个包含缓存名称的白名单。通过使用 `keys()` 方法来返回 `CacheStorage` 对象中的 keys 集合，然后检查缓存 key 是否在白名单中，如果不存在，则使用 `CacheStorage.delete` 方法来删除该缓存。

```js
this.addEventListener('activate', function(event) {
  var cacheWhitelist = ['v2'];

  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheWhitelist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      });
    })
  );
});
```

规范
--

| Specification |
| --- |
| Service Workers # cache-storage-keys |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

亦可参考
----

* Using Service Workers (en-US)
* `Cache`
* `WorkerGlobalScope.caches` (en-US)


CacheStorage.match() - Web API 接口参考

CacheStorage.match()
====================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

`CacheStorage` 接口 (可适用于全局性`caches`) 的 **`match()`** 方法检查给定的`Request` 对象或 url 字符串是否是一个已存储的 `Response` 对象的 key. 这个方法针对 `Response` 返回一个 `Promise` ，如果没有匹配则返回 `undefined` 。

cache 对象按创建顺序查询。

**备注：** `caches.match()` 是一个便捷方法。其作用等同于在每个缓存上调用 `cache.match()` 方法（按照`caches.keys()`返回的顺序) 直到返回`Response` 对象。

语法
--
```

caches.match(request, options).then(function(response) {
  // Do something with the response
});
```

### 参数

request

想要匹配的 `Request`。这个参数可以是 `Request` 对象或 URL 字符串。

options 可选

这个对象中的属性控制在匹配操作中如何进行匹配选择。可选择参数如下：

* `ignoreSearch`: `Boolean` (en-US)值，指定匹配过程是否应该忽略 url 中查询参数。举个例子，如果该参数设置为`true`, 那么 `?value=bar` 作为 `http://foo.com/?value=bar` 中的查询参数将会在匹配过程中被忽略。该参数默认 `false`。
* `ignoreMethod`: `Boolean` (en-US) 值，当被设置为 `true` 时，将会阻止在匹配操作中对 `Request`请求的 `http` 方式的验证 (通常只允许 `GET` 和 `HEAD` 两种请求方式)。该参数默认为 `false`.
* `ignoreVary`: `Boolean` (en-US) 值，当该字段被设置为 `true`, 告知在匹配操作中忽略对`VARY`头信息的匹配。换句话说，当请求 URL 匹配上，你将获取匹配的 `Response` 对象，无论请求的 `VARY` 头存在或者没有。该参数默认为 `false`.
* `cacheName`: `DOMString` 值，表示所要搜索的缓存名称。

### 返回值

返回 resolve 为匹配 `Response` 的 `Promise` 对象。如果没有与指定 request 相匹配 response，promise 将使用 `undefined` resolve.

例子
--

此示例来自于 MDN sw-test example （请参阅 sw-test running live）。这里，等待 `FetchEvent` 事件触发。我们构建自定义响应，像这样：

1. 使用 `CacheStorage.match()` 检查 `CacheStorage` 中是否存在匹配请求，如果存在，则使用它。
2. 如果没有，使用 `open()` 打开 `v1` cache，使用 `Cache.put()` 将默认网络请求放入 cache 中，并只用 `return response.clone()` 返回默认网络请求的克隆副本。最后一个是必须的，因为 `put()` 使用响应主体。
3. 如果此操作失败（例如，因为网络已关闭），则返回备用响应。

```js
caches
  .match(event.request)
  .then(function (response) {
    return (
      response ||
      fetch(event.request).then(function (r) {
        caches.open("v1").then(function (cache) {
          cache.put(event.request, r);
        });
        return r.clone();
      })
    );
  })
  .catch(function () {
    return caches.match("/sw-test/gallery/myLittleVader.jpg");
  });
```

规范
--

| Specification |
| --- |
| Service Workers # cache-storage-match |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

亦可参考
----

* Using Service Workers (en-US)
* `Cache`
* `self.caches` (en-US)


CacheStorage.delete() - Web API 接口参考

CacheStorage.delete()
=====================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

`CacheStorage` 接口的 **`delete()`** 方法查找匹配 `cacheName` 的 `Cache` 对象，如果找到，则删除 `Cache` 对象并返回一个 resolve 为 true 的 `Promise` . 如果未找到 `Cache` 对象，则返回 `false`.

语法
--
```

caches.delete(cacheName).then(function(true) {
  //your cache is now deleted
});
```

### Returns

如果找到 `Cache` 对象，删除它，返回一个 resolve 为 `true` 的 `Promise` ，否则，返回 `false` .

### Parameters

cacheName

想要删除的 cache 对象的名称。

实例
--

在此代码片段中，我们等待一个 activate 事件，然后运行一个 `waitUntil()` 块，其在一个新的 service worker 被激活前清除所有旧的、未使用的 cache. 这里我们有一个白名单，其中包含我们想要保留的 cache 的 name. 我们使用 `CacheStorage.keys` 返回 `CacheStorage` 对象中 cache 的键，然后检查每个键值，以查看它是否在白名单中。如果没有，我们使用 `delete()` 删除它。

```js
this.addEventListener("activate", function (event) {
  var cacheWhitelist = ["v2"];

  event.waitUntil(
    caches.keys().then(function (keyList) {
      return Promise.all(
        keyList.map(function (key) {
          if (cacheWhitelist.indexOf(key) === -1) {
            return caches.delete(key);
          }
        }),
      );
    }),
  );
});
```

规范
--

| Specification |
| --- |
| Service Workers # cache-storage-delete |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Using Service Workers (en-US)
* `Cache`
* `WorkerGlobalScope.caches` (en-US)


CacheStorage.has() - Web API 接口参考

CacheStorage.has()
==================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

`CacheStorage` 对象的 \*\*`has()`\*\*方法返回一个 `Promise` 对象，当 `Cache` 对象有 `cacheName` 时被处理为 `true` 。

语法
--
```

caches.has(cacheName).then(function(boolean) {
  // true: 缓存存在
});
```

### 返回值

返回一个 `Promise` 对象，缓存存在时 resolve 的布尔值为 `true` 否则为 `false` 。

### 参数

cacheName

一个表示你正在 `CacheStorage` 中查找的 `Cache` 对象 name 的 `DOMString`.

例子
--

在下面的例子中首先检测是否有名为 v1 的缓存存在，如果存在我们会向其添加内容，，如果不存在我们会做些对应的初始化动作。

```js
caches
  .has("v1")
  .then(function (hasCache) {
    if (!hasCache) {
      someCacheSetupfunction();
    } else {
      caches.open("v1").then(function (cache) {
        return cache.addAll(myAssets);
      });
    }
  })
  .catch(function () {
    // 处理异常
  });
```

规范
--

| Specification |
| --- |
| Service Workers # cache-storage-has |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参考
--

* Using Service Workers (en-US)
* `Cache`
* `WorkerGlobalScope.caches` (en-US)


CacheStorage.open() - Web API 接口参考

CacheStorage.open()
===================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

`CacheStorage` 接口的 **`open()`** 方法返回一个 resolve 为匹配 `cacheName` 的 `Cache` 对象的 `Promise` .

**备注：** 如果指定的 `Cache` 不存在，则使用该 `cacheName` 创建一个新的 cache，并返回一个 resolve 为该新 `Cache` 对象的`Promise`.

语法
--

```js
// "caches" is a global read-only variable, which is an instance of CacheStorage,
// For more info, refer to: https://developer.mozilla.org/zh-CN/docs/Web/API/caches

caches.open(cacheName).then(function (cache) {
  // Do something with your cache
});
```

### 参数

cacheName

要打开的 cache 对象 name.

### 返回值

一个 resolve 为请求的 `Cache` 对象的 `Promise` .

示例
--

此示例来自于 MDN sw-test example （请参阅 sw-test running live）。这里，等待 `FetchEvent` 事件触发。我们构建自定义响应，像这样：

1. 使用 `CacheStorage.match()` 检查 `CacheStorage` 中是否存在匹配请求，如果存在，则使用它。
2. 如果没有，使用 `CacheStorage.open()` 打开 `v1` cache，使用 `Cache.put()` 将默认网络请求放入 cache 中，并使用 `return response.clone()` 返回默认网络请求的克隆副本。最后一个是必须的，因为 `put()` 使用响应主体。
3. 如果此操作失败（例如，因为网络已关闭），则返回备用响应。

```js
var cachedResponse = caches
  .match(event.request)
  .catch(function () {
    return fetch(event.request);
  })
  .then(function (response) {
    caches.open("v1").then(function (cache) {
      cache.put(event.request, response);
    });
    return response.clone();
  })
  .catch(function () {
    return caches.match("/sw-test/gallery/myLittleVader.jpg");
  });
```

规范
--

| Specification |
| --- |
| Service Workers # cache-storage-open |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Using Service Workers (en-US)
* `Cache`
* `WorkerGlobalScope.caches` (en-US)


NodeList.length - Web API 接口参考

NodeList.length
===============

摘要
--

返回 `NodeList` 集合中子节点数量。

语法
--

```js
numItems = nodeList.length;
```

* `numItems` 是一个整数，表示 `NodeList` 子节点的数量。
例子
--

```js
// 获取文档中的所有 p 标签
var items = document.getElementsByTagName("p");

// 循环 items 然后输出每个 p 标签 html
var gross = "";
for (var i = 0; i < items.length; i++) {
  gross += items[i].innerHTML;
}

// gross 现在集合了所有 p 标签的 HTML 内容。
```

注意
--

length 不是 元素（Element）的属性，而是 NodeList 的属性。NodeList 是使用 DOM 操作方法返回的对象，比如使用 document.getElementsByTagName 就会返回一个 NodeList 对象。

length 是在 DOM 操作中非常常见的属性。最常见的是用 length 属性来判断某些节点是否存在，或者如上述一样，用在 for 循环上。

规范
--

length


NodeList.values() - Web API 接口参考

NodeList.values()
=================

该方法返回一个 iterator 迭代器，可以利用迭代器遍历所有 value。

Syntax
------
```

nodeList.values();
```

### Return value

Returns an `iterator`.

Example
-------

```js
var node = document.createElement("div");
var kid1 = document.createElement("p");
var kid2 = document.createTextNode("hey");
var kid3 = document.createElement("span");

node.appendChild(kid1);
node.appendChild(kid2);
node.appendChild(kid3);

var list = node.childNodes;

// Using for..of
for (var value of list.values()) {
  console.log(value);
}
```

The result is:
```

<p>
#text "hey"
<span>
```

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `Node`
* `NodeList`


NodeList.prototype.forEach() - Web API 接口参考

NodeList.prototype.forEach()
============================

`NodeList`接口的 **`forEach()`** 方法按插入顺序为列表中的每个值对调用一次参数中给定的回调。

语法
--
```

someNodeList.forEach(callback[, thisArg]);
```

### 参数

`callback`

为 `someNodeList`的每一个元素执行函数。它接受以下三个参数：

`currentValue`

`someNodeList`中的当前元素。

`currentIndex` 可选

`someNodeList`中的`currentValue`所对应的索引值。

`listObj` 可选

`someNodeList` 在 `forEach()` 方法中所属的 NodeList 对象。

`thisArg` 可选

传递 `callback` 的值一般用`this`值。

### 返回值

`undefined`.

示例
--

```js
let node = document.createElement("div");
let kid1 = document.createElement("p");
let kid2 = document.createTextNode("hey");
let kid3 = document.createElement("span");

node.appendChild(kid1);
node.appendChild(kid2);
node.appendChild(kid3);

let list = node.childNodes;

list.forEach(function (currentValue, currentIndex, listObj) {
  console.log(currentValue + ", " + currentIndex + ", " + this);
}, "myThisArg");
```

上述代码会产生以下结果：
```

[object HTMLParagraphElement], 0, myThisArg
[object Text], 1, myThisArg
[object HTMLSpanElement], 2, myThisArg
```

规范
--

| Specification |
| --- |
| DOM Standard # interface-nodelist |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `Node`
* `NodeList`


NodeList.item - Web API 接口参考

NodeList.item
=============

### 概述

根据给定的索引，返回一个 `NodeList`对象中包含的 Node 对象。

### 语法
```

nodeItem = nodeList.item(index)
```

* `nodeList`是一个`NodeList`对象。通常是由某个 DOM 属性或方法返回的，比如childNodes.
* `index` 是给定的索引。从 0 开始。
* `nodeItem` 是根据索引`index` 由 `item` 方法从 `nodeList` 中获取到的节点。

JavaScript 有更简单的语法来从一个 NodeList 对象中获取指定索引的节点：
```

nodeItem = nodeList[index]
```

### 例子
```

var tables = document.getElementsByTagName("table");
var firstTable = tables.item(1); // 或者简写为 tables[1]，返回一个文档中的第二个 table 元素。
```

### 备注

如果索引越界，该方法不会抛出异常，只会返回 `null`。

`item()` 不是 DOM 元素或者 DOM 节点的方法，而是 NodeList 对象的方法。

### 规范

DOM Level 1 Core: NodeList.item()

DOM Level 2 Core: NodeList.item()


NodeList.keys() - Web API 接口参考

NodeList.keys()
===============

**`NodeList.keys()`** 方法返回 `iterator` ，此方法允许遍历这个对象中包含的所有的键，即使这个键是 `unsigned integer（无符号整数）`.

语法
--
```

nodeList.keys();
```

### 返回值

返回 `iterator`.

例子
--

```js
var node = document.createElement("div");
var kid1 = document.createElement("p");
var kid2 = document.createTextNode("hey");
var kid3 = document.createElement("span");

node.appendChild(kid1);
node.appendChild(kid2);
node.appendChild(kid3);

var list = node.childNodes;

// Using for..of
for (var key of list.keys()) {
  console.log(key);
}
```

结果是：
```

0
1
2
```

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

相关链接
----

* `Node`
* `NodeList`


NodeList.entries() - Web API 接口参考

NodeList.entries()
==================

该方法返回一个迭代协议，允许遍历此对象中包含的所有键/值。该值也是一个`Node` 对象。

语法
--
```

list.entries();
```

### 返回值

返回一个 `iterator`.

例子
--

```js
var node = document.createElement("div");
var kid1 = document.createElement("p");
var kid2 = document.createTextNode("hey");
var kid3 = document.createElement("span");
node.appendChild(kid1);
node.appendChild(kid2);
node.appendChild(kid3);

var list = node.childNodes;

// 使用 for..of 循环
for (var entry of list.entries()) {
  console.log(entry);
}
```

结果如下：
```

Array [ 0, <p> ]
Array [ 1, #text "hey" ]
Array [ 2, <span> ]
```

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

相关链接
----

* `Node`
* `NodeList`


ExtendableEvent.waitUntil() - Web API 接口参考

ExtendableEvent.waitUntil()
===========================

**`ExtendableEvent.waitUntil()`** 方法告诉事件分发器该事件仍在进行。这个方法也可以用于检测进行的任务是否成功。在服务工作线程中，这个方法告诉浏览器事件一直进行，直至 promise 解决，浏览器不应该在事件中的异步操作完成之前终止服务工作线程。

service worker 中的 `install` (en-US) 事件使用 `waitUntil()` 来将服务工作线程保持在 `installing` (en-US) 阶段。如果传入 `waitUntil()` 的 promise 被拒绝，则将此次安装视为失败，丢弃这个服务工作线程。这主要用于确保在服务工作线程安装以前，所有依赖的核心缓存都已经成功载入。

service worker 中的 `activate` (en-US) 事件使用 `waitUntil()` 来延迟函数事件，如 `fetch` 和 `push`，直至传入 `waitUntil()` 的 promise 被解决。这让服务工作线程有时间更新数据库架构（database schema）和删除过时缓存，让其他事件能在一个完成更新的状态下进行。

`waitUntil()` 方法最初必须在事件回调里调用，在此之后，方法可以被调用多次，直至所有传入的 promise 被解决。

**备注：** 上述段落描述的行为已经在 Firefox 43 中被修复（参见 Firefox bug 1180274）。

语法
--

```js
waitUntil(promise)
```

### 参数

一个 `Promise`.

示例
--

在服务工作线程的 `install` 事件中使用 `waitUntil()`。

```js
addEventListener("install", (event) => {
  const preCache = async () => {
    const cache = await caches.open("static-v1");
    return cache.addAll(["/", "/about/", "/static/styles.css"]);
  };
  event.waitUntil(preCache());
});
```

规范
--

| Specification |
| --- |
| Service Workers # dom-extendableevent-waituntil |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 使用 Service Worker
* 是否支持 ServiceWorker
* `Promise`


使用历史记录 API - Web API 接口参考

使用历史记录 API
==========

历史记录 API 使网站能够与浏览器的会话历史记录（即用户在给定窗口中访问过的页面列表）进行交互。当用户通过点击链接等方式访问新页面时，这些新页面就会被添加到会话历史记录中。用户还可以使用浏览器的“后退”和“前进”按钮在历史记录中来回移动。

历史记录 API 中定义的主要接口是 `History` 接口，它定义了两套截然不同的方法：

1. 在会话历史记录中导航到页面的方法：
    * `History.back()`
    * `History.forward()`
    * `History.go()`
2. 修改会话历史记录的方法：
    * `History.pushState()`
    * `History.replaceState()`

在本指南中，我们将只关注第二组方法，因为这些方法的行为更为复杂。

`pushState()` 方法会向会话历史记录添加新条目，而 `replaceState()` 方法则会更新当前页面的会话历史记录条目。这两种方法都接收一个 `state` 参数，该参数可包含任何可序列化对象。当浏览器导航到该历史条目时，浏览器会触发 `popstate` 事件，其中包含与该条目相关的状态对象。

这些 API 的主要目的是支持像单页应用这样的网站，它们使用 JavaScript API（如 `fetch()`）来更新页面的新内容，而不是加载整个新页面。

单页应用和会话历史记录
-----------

传统上，网站是以页面集合的形式实现的。当用户通过点击链接浏览网站的不同部分时，浏览器每次都会加载一个全新的页面。

虽然这对许多网站来说很好，但也有一些缺点：

* 如果只需要更新页面的一部分，每次加载整个页面的效率会很低。
* 跨页面导航时很难保持应用程序状态

由于这些原因，单页应用（SPA）是一种流行的 web 应用程序模式，在这种模式中，网站由一个页面组成，当用户点击链接时，页面会：

1. 阻止加载新页面的默认行为
2. 获取要显示的新内容
3. 用新内容更新页面

例如：

```js
document.addEventListener("click", async (event) => {
  const creature = event.target.getAttribute("data-creature");
  if (creature) {
    // 阻止新页面加载
    event.preventDefault();
    try {
      // 获取新内容
      const response = await fetch(`creatures/${creature}.json`);
      const json = await response.json();
      // 用新内容更新页面
      displayContent(json);
    } catch (err) {
      console.error(err);
    }
  }
});
```

在该单击处理器中，如果链接包含数据属性 `"data-creature"`，我们就会使用该属性的值来获取包含页面新内容的 JSON 文件。

JSON 文件可能如下所示：

```json
{
  "description": "Bald eagles are not actually bald.",
  "image": {
    "src": "images/eagle.jpg",
    "alt": "A bald eagle"
  },
  "name": "Eagle"
}
```

`displayContent()` 函数使用 JSON 内容更新页面：

```js
// 使用新内容更新页面
function displayContent(content) {
  document.title = `生物：${content.name}`;

  const description = document.querySelector("#description");
  description.textContent = content.description;

  const photo = document.querySelector("#photo");
  photo.setAttribute("src", content.image.src);
  photo.setAttribute("alt", content.image.alt);
}
```

问题在于，它破坏了浏览器“后退”和“前进”按钮的预期行为。

从用户的角度来看，他们点击了一个链接，页面就更新了，所以看起来像是一个新页面。如果用户按下浏览器的“后退”按钮，就会返回到点击链接之前的状态。

但在浏览器看来，最后一个链接并没有加载新页面，因此“后退”按钮会将浏览器带回到用户打开 SPA 之前加载的页面。

这正是 `pushState()`、`replaceState()` 和 `popstate` 事件所要解决的问题。它们使我们能够合成历史条目，并在当前会话历史条目更改为这些条目之一（例如，由于用户按下了“后退”或“前进”按钮）时收到通知。

使用 `pushState()`
----------------

我们可以像下面这样在单击事件处理器中添加历史记录条目：

```js
document.addEventListener("click", async (event) => {
  const creature = event.target.getAttribute("data-creature");
  if (creature) {
    event.preventDefault();
    try {
      const response = await fetch(`creatures/${creature}.json`);
      const json = await response.json();
      displayContent(json);
      // 向历史记录中添加新条目
      // 模拟了新页面的加载
      history.pushState(json, "", creature);
    } catch (err) {
      console.error(err);
    }
  }
});
```

这里，我们使用三个参数调用了 `pushState()`：

* `json`：这是我们刚刚获取的内容。它将与历史条目一起存储，之后将作为 `state` (en-US) 属性包含在传递给 `popstate` 事件处理器的参数中。
* `""`：这是与传统网站向后兼容所必需的，应始终为空字符串。
* `creature`：这将用作条目的 URL。它将显示在浏览器的 URL 栏中，并在页面发出的任何 HTTP 请求中用作 `Referer` 标头的值。请注意，这必须与页面同源 。
使用 `popstate` 事件
----------------

假设用户：

1. 点击了 SPA 中的一个链接，因此我们使用 `pushState()` 更新页面并添加历史记录条目 A
2. 点击 SPA 中的另一个链接，因此我们使用 `pushState()` 更新页面并添加历史记录条目 B
3. 按下“后退”按钮

现在，新的当前历史记录条目是 A，因此浏览器触发了 `popstate` 事件，事件处理器参数包括我们在处理导航到 A 时传递给 `pushState()` 的 JSON，这意味着我们可以通过这样的事件处理器来恢复正确的内容：

```js
// 处理前进/回退按钮
window.addEventListener("popstate", (event) => {
  // 如果提供了一个状态（state），我们“模拟”一个页面并更新当前页面
  if (event.state) {
    // 模拟前一个页面加载
    displayContent(event.state);
  }
});
```

使用 `replaceState()`
-------------------

我们还需要添加一项内容。当用户加载 SPA 时，浏览器会添加一个历史记录条目。由于这是一次实际的页面加载，因此该条目没有与之相关的状态。因此，假设用户：

1. 加载 SPA：浏览器添加历史记录条目
2. 点击 SPA 中的一个链接：点击处理程序会更新页面，并使用 `pushState()` 添加一个历史记录条目
3. 按下“后退”按钮

现在我们想回到 SPA 的初始状态，但由于这是同一文档中的导航，页面不会被重新加载，而且初始页面的历史条目没有状态，我们无法使用 `popstate` 恢复它。

解决办法是使用 `replaceState()` 为初始页面设置状态对象。例如：

```js
// 在页面加载时创建状态，并用其替换当前历史记录
const image = document.querySelector("#photo");
const initialState = {
  description: document.querySelector("#description").textContent,
  image: {
    src: image.getAttribute("src"),
    alt: image.getAttribute("alt"),
  },
  name: "Home",
};
history.replaceState(initialState, "", document.location.href);
```

在页面加载时，我们会收集页面的所有部分，以便在用户返回 SPA 的起点时还原这些部分。这与我们在处理其他导航时获取的 JSON 结构相同。我们将这个 `initialState` 对象传递给 `replaceState()`，这样就能有效地将状态对象添加到当前历史记录条目中。

当用户返回到我们的起点时，`popstate` 事件将包含此初始状态，我们可以使用 `displayContent()` 函数更新页面。

一个完整的示例
-------

你可以在 https://github.com/mdn/dom-examples/tree/main/history-api 中看到完整的示例，并在 https://mdn.github.io/dom-examples/history-api/ 访问到实时运行的版本。

参见
--

* 历史记录 API
* `history` 全局对象


CustomElementRegistry.define() - Web API 接口参考

CustomElementRegistry.define()
==============================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
`CustomElementRegistry`接口的 **`define()`** 方法定义了一个自定义元素。

你可以创建两种类型的自定义元素：

* **自主定制元素**：独立元素; 它们不会从内置 HTML 元素继承。
* **自定义内置元素**：这些元素继承自 - 并扩展 - 内置 HTML 元素
语法
--
```

customElements.define(name, constructor, options);
```

### 参数

name

自定义元素名。

constructor

自定义元素构造器。

options 可选

控制元素如何定义。目前有一个选项支持：

* `extends`. 指定继承的已创建的元素。被用于创建自定义元素。

### 返回值

空

示例
--

### 自主定制元素

以下代码取自我们的 popup-info-box-web-component 示例（查看实时运行版本）。

```js
// Create a class for the element
class PopUpInfo extends HTMLElement {
  constructor() {
    // Always call super first in constructor
    super();

    // Create a shadow root
    var shadow = this.attachShadow({ mode: "open" });

    // Create spans
    var wrapper = document.createElement("span");
    wrapper.setAttribute("class", "wrapper");
    var icon = document.createElement("span");
    icon.setAttribute("class", "icon");
    icon.setAttribute("tabindex", 0);
    var info = document.createElement("span");
    info.setAttribute("class", "info");

    // Take attribute content and put it inside the info span
    var text = this.getAttribute("text");
    info.textContent = text;

    // Insert icon
    var imgUrl;
    if (this.hasAttribute("img")) {
      imgUrl = this.getAttribute("img");
    } else {
      imgUrl = "img/default.png";
    }
    var img = document.createElement("img");
    img.src = imgUrl;
    icon.appendChild(img);

    // Create some CSS to apply to the shadow dom
    var style = document.createElement("style");

    style.textContent =
      ".wrapper {" +
      "position: relative;" +
      "}" +
      ".info {" +
      "font-size: 0.8rem;" +
      "width: 200px;" +
      "display: inline-block;" +
      "border: 1px solid black;" +
      "padding: 10px;" +
      "background: white;" +
      "border-radius: 10px;" +
      "opacity: 0;" +
      "transition: 0.6s all;" +
      "position: absolute;" +
      "bottom: 20px;" +
      "left: 10px;" +
      "z-index: 3;" +
      "}" +
      "img {" +
      "width: 1.2rem" +
      "}" +
      ".icon:hover + .info, .icon:focus + .info {" +
      "opacity: 1;" +
      "}";

    // attach the created elements to the shadow dom

    shadow.appendChild(style);
    shadow.appendChild(wrapper);
    wrapper.appendChild(icon);
    wrapper.appendChild(info);
  }
}

// Define the new element
customElements.define("popup-info", PopUpInfo);
```

```html
<popup-info
 img="img/alt.png"
 text="Your card validation code (CVC) is an extra
 security feature — it is the last 3 or 4
 numbers on the back of your card."></popup-info>
```

**备注：** 自主自定义元素的构造函数必须扩展`HTMLElement`。

### 自定义内置元素

以下代码取自我们的 word-count-web-component 实例（查看实例效果）。

```js
// Create a class for the element
class WordCount extends HTMLParagraphElement {
  constructor() {
    // Always call super first in constructor
    super();

    // count words in element's parent element
    var wcParent = this.parentNode;

    function countWords(node) {
      var text = node.innerText || node.textContent;
      return text.split(/\s+/g).length;
    }

    var count = "Words: " + countWords(wcParent);

    // Create a shadow root
    var shadow = this.attachShadow({ mode: "open" });

    // Create text node and add word count to it
    var text = document.createElement("span");
    text.textContent = count;

    // Append it to the shadow root
    shadow.appendChild(text);

    // Update count when element content changes
    setInterval(function () {
      var count = "Words: " + countWords(wcParent);
      text.textContent = count;
    }, 200);
  }
}

// Define the new element
customElements.define("word-count", WordCount, { extends: "p" });
```

```html
<p is="word-count"></p>
```

规范
--

| Specification |
| --- |
| HTML Standard # dom-customelementregistry-define-dev |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


CustomElementRegistry.upgrade() - Web API 接口参考

CustomElementRegistry.upgrade()
===============================

CustomElementRegistry 接口的 upgrade() 方法将更新节点子树中所有包含阴影的自定义元素，甚至在它们连接到主文档之前也是如此。

语法
--
```

customElements.upgrade(root);
```

### 参数

`root`

待升级的包含阴影的派生元素`节点` 。如果没有可升级的派生实例，则不会抛出异常。

### 返回值

空。

示例
--

摘至HTML 规范:

```js
const el = document.createElement("spider-man");

class SpiderMan extends HTMLElement {}
customElements.define("spider-man", SpiderMan);

console.assert(!(el instanceof SpiderMan)); // not yet upgraded

customElements.upgrade(el);
console.assert(el instanceof SpiderMan); // upgraded!
```

规范
--

| Specification |
| --- |
| HTML Standard # dom-customelementregistry-upgrade-dev |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


CustomElementRegistry.get() - Web API 接口参考

CustomElementRegistry.get()
===========================

`CustomElementRegistry` 的 **`get()`** 方法返回以前定义自定义元素的构造函数。

语法
--
```

constructor = customElements.get(name);
```

### 参数

name

你想要返回引用的构造函数的自定义元素的名字。

### 返回值

指定名字的自定义元素的构造函数，如果没有使用该名称的自定义元素定义，则为`undefined`。

例子
--

```js
customElements.define(
  "my-paragraph",
  class extends HTMLElement {
    constructor() {
      super();
      let template = document.getElementById("my-paragraph");
      let templateContent = template.content;

      const shadowRoot = this.attachShadow({ mode: "open" }).appendChild(
        templateContent.cloneNode(true),
      );
    }
  },
);

// Return a reference to the my-paragraph constructor
let ctor = customElements.get("my-paragraph");
```

规范
--

| Specification |
| --- |
| HTML Standard # dom-customelementregistry-get-dev |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


CustomElementRegistry.whenDefined() - Web API 接口参考

CustomElementRegistry.whenDefined()
===================================

当一个元素被定义时`CustomElementRegistry` 中的方法\*\*`whenDefined()`\*\* 接口返回 `Promise`.

语法
--
```

Promise<> customElements.whenDefined(name);
```

### 参数

name

自定义元素的名称

### 返回值

当自定义元素被定义时一个`Promise` 返回{jsxref("undefined")}}. 如果自定义元素已经被定义，则 resolve 立即执行。

### 例外

| Exception | Description |
| --- | --- |
| `SyntaxError` | 如果提供的 **name** 不是一个有效的 自定义元素名字, promise 的 reject 回调会接收到一个 `SyntaxError`. |

例子
--

这个例子使用 `whenDefined()` 来检测生成菜单的自定义元素何时被定义。这个菜单显示占位符内容一直到菜单内容已经准备好显示。

```html
<nav id="menu-container">
  <div class="menu-placeholder">Loading...</div>
  <nav-menu>
    <menu-item>Item 1</menu-item>
    <menu-item>Item 2</menu-item>
    ...
    <menu-item>Item N</menu-item>
  </nav-menu>
</nav>
```

```js
const container = document.getElementById("menu-container");
const placeholder = container.querySelector(".menu-placeholder");
// Fetch all the children of menu that are not yet defined.
const undefinedElements = container.querySelectorAll(":not(:defined)");

const promises = [...undefinedElements].map((button) =>
  customElements.whenDefined(button.localName),
);

// Wait for all the children to be upgraded,
// then remove the placeholder.
await Promise.all(promises);
container.removeChild(placeholder);
```

规范
--

| Specification |
| --- |
| HTML Standard # dom-customelementregistry-whendefined-dev |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


Bluetooth.requestDevice() - Web API 接口参考

Bluetooth.requestDevice()
=========================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

`Bluetooth`接口的 **`Bluetooth.requestDevice()`** 方法返回一个带有对应 options 的`BluetoothDevice` (en-US)对象的`Promise`对象。如果没有蓝牙设备选择界面，则此方法返回与条件匹配的第一个设备。

语法
--
```

Bluetooth.requestDevice(options).then(function(bluetoothDevice) { ... })
```

### 返回值

带有`BluetoothDevice` (en-US) 对象的`Promise` 对象。

### 参数

options

设置设备请求选项的对象。可用的选项是：

* `filters[]`: 一个 BluetoothScanFilters 数组。此过滤器由一个 BluetoothServiceUUID 数组，一个名称参数和一个 namePrefix 参数组成。
* `optionalServices[]`: 一个 BluetoothServiceUUID 数组。
* `acceptAllDevices`: boolean 表示请求脚本可以接受所有蓝牙设备。默认值为 false。

示例
--
```

//扫描选项匹配任何设备广播：

//. 标准心率服务。

//. 两个 16 位服务 ID 0x1802 和 0x1803。

//. 专有的 128 位 UUID 服务 c48e6067-5295-48d3-8d5c-0395f61792b1。

//. 名称为“设备名”的设备。

//. 名称以“前缀”开头的设备。

//

//如果设备包含电池服务，

//即使设备不通告该服务，也可以访问电池服务。

let options = {
  filters: [
    {services: ['heart_rate']},
    {services: [0x1802, 0x1803]},
    {services: ['c48e6067-5295-48d3-8d5c-0395f61792b1']},
    {name: '设备名'},
    {namePrefix: '前缀'}
  ],
  optionalServices: ['battery_service']
}

navigator.bluetooth.requestDevice(options).then(function(device) {
  console.log('名称：' + device.name);
  // 在此处实现设备调用
})
.catch(function(error) {
  console.log("出现错误： " + error);
});
```

查看详细示例

规范
--

| Specification |
| --- |
| Web Bluetooth # dom-bluetooth-requestdevice |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


IdleDeadline.timeRemaining() - Web API 接口参考

IdleDeadline.timeRemaining()
============================

`IdleDeadline` 接口上的 **`timeRemaining()`** 方法返回当前空闲期间剩余的估计毫秒数。回调函数可以在任何时候调用此方法，以确定在必须返回之前它可以继续工作多长时间。例如，如果回调完成了一个任务，并且有另一个任务要开始，它可以调用 `timeRemaining()` 来查看是否有足够的时间来完成下一个任务。如果没有，回调函数可以立即返回，或者用剩余的时间寻找其他合适的工作。

当 `timeRemaining()` 达到 0 时，建议回调应该将控制权返回给用户代理的事件循环。

语法
--

```js
timeRemaining = IdleDeadline.timeRemaining();
```

### 返回值

一个 `DOMHighResTimeStamp` 值（是一个浮点数），表示用户代理估计在当前空闲期间内剩余的毫秒数。理想情况下，该值精确到大约 5 微秒。

如果 `IdleDeadline` 对象的 `didTimeout` (en-US) 属性为 `true` ，该方法返回 `0`。

示例
--

在文章后台任务协作调度 API 中可以查看完整示例 。

规范
--

| Specification |
| --- |
| requestIdleCallback() Cooperative Scheduling of Background Tasks # dom-idledeadline-timeremaining |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 后台任务协作调度
* `IdleDeadline`
* `Window.requestIdleCallback()`
* `Window.cancelIdleCallback()`


DOMRect.DOMRect() - Web API 接口参考

DOMRect.DOMRect()
=================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

**`DOMRect()`** 构造函数生成一个新的 `DOMRect` 对象。

语法
--

```js
var myDOMRect = new DOMRect(x, y, width, height);
```

### 参数

x

`DOMRect` 原点的 `x` 坐标。

y

`DOMRect` 原点的 y 坐标。

width

`DOMRect` 的宽度。

height

`DOMRect` 的高度。

例子
--

想生成一个新 `DOMRect`，你可以运行一行这样的代码：

```js
myDOMRect = new DOMRect(0, 0, 100, 100);
// running 'myDOMRect' in the console would then return
// DOMRect { x: 0, y: 0, width: 100, height: 100, top: 0, right: 100, bottom: 100, left: 0 }
```

规范
--

| Specification |
| --- |
| Geometry Interfaces Module Level 1 # dom-domrect-domrect |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

更多
--

* `DOMPoint`
* `DOMRect`


DOMPoint.x - Web API 接口参考

DOMPoint.x
==========

**`DOMPoint`** 的 x 属性表示该点的水平坐标。通常， `x` 的正值表示右边，负值表示左边，除非改变了默认的轴方向。

语法
--
```

var xPos = DOMPoint.x;
```

### 值

双精度浮点值，表示该点的 x 坐标值。这个值的类型并没有严格限制，意味着它可以是 `NaN` 或 `±Infinity`。

规范
--

| Specification |
| --- |
| Geometry Interfaces Module Level 1 # dom-dompointreadonly-x |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 其他坐标属性：`y`，`z`，透视值 `w`.


DOMPoint.y - Web API 接口参考

DOMPoint.y
==========

**`DOMPoint`** 的 **`y`** 属性表示该点的垂直坐标。`y` 值增加表示向下偏移，减小表示向上偏移，除非改变了默认轴方向。

语法
--
```

var yPos = DOMPoint.y;
```

### 值

双精度浮点值，表示该点的 y 坐标值。这个值的类型并没有严格限制，意味着它可以是 `NaN` 或 `±Infinity`。

规范
--

| Specification |
| --- |
| Geometry Interfaces Module Level 1 # dom-dompointreadonly-y |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 其他坐标属性： `x`, `z`, 透视值 `w`。


DOMPoint.DOMPoint() - Web API 接口参考

DOMPoint.DOMPoint()
===================

\*\*`DOMPoint()`\*\*构造函数创建并返回一个 `DOMPoint` 对象，可提供部分或全部属性值作为其参数。

也可以通过调用静态方法 `DOMPoint.fromPoint()` (en-US) 来创建 `DOMPoint` 。此方法接受一个 `DOMPointInit` (en-US) 兼容对象（`DOMPoint` 或 `DOMPointReadOnly` (en-US)）作为参数。

语法
--
```

point = new DOMPoint(x, y, z, w);
```

### 参数

`x` 可选

`x` 坐标。

`y` 可选

`y` 坐标。

`z` 可选

`z` 坐标。

`w` 可选

透视值。

示例
--

示例首先创建了一个表示当前窗口左上角的 `DOMPoint` ，接着根据第一个点创建一个新的 `DOMPoint` 并将其在垂直和水平方向上偏移 100px。

```js
var windTopLeft = new DOMPoint(window.screenX, window.screenY);
var newTopLeft = DOMPoint.fromPoint(windTopLeft);
newTopLeft.x += 100;
newTopLeft.y += 100;
```

规范
--

| Specification |
| --- |
| Geometry Interfaces Module Level 1 # dom-dompoint-dompoint |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `DOMPointReadOnly()`
* `DOMRect`
* `DOMMatrix`


DOMPoint.w - Web API 接口参考

DOMPoint.w
==========

**`DOMPoint`** 的 w 属性表示该点的空间透视值。

语法
--
```

var perspective = DOMPoint.w;
```

### 值

双精度浮点值，表示该点的空间透视值。这个值的类型并没有严格限制，意味着它可以是 `NaN` 或 `±Infinity`。默认值为 1.0。

规范
--

| Specification |
| --- |
| Geometry Interfaces Module Level 1 # dom-dompointreadonly-w |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 其他坐标属性：`x`，`y`，`z`.


DOMPoint.z - Web API 接口参考

DOMPoint.z
==========

**`DOMPoint`** 的 **`z`** 属性表示该点的深度坐标。 `z` 值为 0 表示屏幕平面，正值表示从屏幕前面向靠近用户的方向延伸，负值表示从屏幕后面向远离用户的方向延伸，除非改变了默认的轴方向。

语法
--
```

var zPos = DOMPoint.z;
```

### 值

双精度浮点值，表示该点的 z 坐标值。这个值的类型并没有严格限制，意味着它可以是 `NaN` 或 `±Infinity`。

规范
--

| Specification |
| --- |
| Geometry Interfaces Module Level 1 # dom-dompointreadonly-z |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 其他坐标属性：`x`， `y`，透视值 `w`。


AbortSignal.timeout() - Web API 接口参考

AbortSignal.timeout()
=====================

静态方法 **`AbortSignal.timeout()`** 返回一个指定时间后将自动中止的 `AbortSignal`。

信号在超时时使用 `TimeoutError` `DOMException` 中止，或者由于按下一个浏览器停止按钮（或者一些内置的“停止”操作）而使用 `AbortError` `DOMException` 中止。这允许 UI 区分超时错误（通常需要通知用户）和用户触发的错误（不需要通知用户）。

超时将基于活动的时间，而不是经过的时间，如果代码在指定的的 worker 中运行或者文档在往返缓存时（bfcache），将有效地暂停。

**备注：** 在编写代码时，无法组合多个信号。意思是你不能使用超时的 signal 或者通过调用 `AbortController.abort()` 直接中止下载。

语法
--

```js
AbortSignal.timeout(time)
```

### 参数

`time`

返回的 `AbortSignal` 将中止之前的“活动”时间（以毫秒为单位）。

### 返回值

一个 `AbortSignal`。

signal 中止，并且 `AbortSignal.reason` 属性将在超时时设置一个 `TimeoutError` `DOMException`，如果操作是用户触发的，则为 `AbortError` `DOMException`。

示例
--

以下简单示例展示了一个 fetch 操作，如果其在 5 秒后不成功，则超时。注意，如果不支持该方法、按下浏览器的“停止”按钮或其他原因，也可能失败。

```js
const url = "https://path\_to\_large\_file.mp4";

try {
  const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
  const result = await res.blob();
  // …
} catch (err) {
  if (err.name === "TimeoutError") {
    console.error("Timeout: It took more than 5 seconds to get the result!");
  } else if (err.name === "AbortError") {
    console.error(
      "Fetch aborted by user action (browser stop button, closing tab, etc.",
    );
  } else if (err.name === "TypeError") {
    console.error("AbortSignal.timeout() method is not supported");
  } else {
    // A network error, or some other problem.
    console.error(`Error: type: ${err.name}, message: ${err.message}`);
  }
}
```

规范
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-abortsignal-timeout① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


AbortSignal.reason - Web API 接口参考

AbortSignal.reason
==================

只读属性 **`reason`** 返回一个使用 JavaScript 值表示的中止原因。

当 signal 没有被中止时，该属性值为 `undefined`。当使用 `AbortController.abort()` 或 `AbortSignal.abort()` 中止 signal 时，可以设置一个指定的值。如果没有在这些方法中明确的设置，则默认是"AbortError"`DOMException`。

值
-

一个使用 JavaScript 值表示的中止原因，如果没有被中止，则是 `undefined`。

示例
--

在以下片段中，我们创建一个新的 `AbortController` 对象，并且获取它的 `AbortSignal`（位于 `signal` 属性中）。然后我们用 `aborted` 属性检查这个信号是否已被中止，并把相应的日志发送给控制台。

```js
const controller = new AbortController();
const signal = controller.signal;

// …

if (signal.aborted) {
  if (signal.reason) {
    console.log(`Request aborted with reason: ${signal.reason}`);
  } else {
    console.log("Request aborted but no reason was given.");
  }
} else {
  console.log("Request not aborted");
}
```

规范
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-abortsignal-reason① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* Fetch API


AbortSignal.abort() - Web API 接口参考

AbortSignal.abort()
===================

静态方法 **`AbortSignal.abort()`** 返回一个已经设置为中止的 `AbortSignal`（并且不会触发 `abort` 事件）。

它是以下片段的简写：

```js
const controller = new AbortController();
controller.abort();
return controller.signal;
```

例如，将它传递给一个 fetch 方法，以运行它的中止逻辑（也就是说，可能其代码的执行顺序是即使预期的 fetch 操作还没有启动，也应该运行中止逻辑）。

**备注：** 该方法在目的上类似于 `Promise.reject`。

语法
--

```js
AbortSignal.abort()
AbortSignal.abort(reason)
```

### 参数

`reason`

操作被中止的原因，可以是任意的 JavaScript 值。如果没有指定原因，则默认设置为"AbortError"`DOMException`。

### 返回值

一个 `AbortSignal.aborted` 属性设置为 `true` 的 `AbortSignal` 实例，并且 `AbortSignal.reason` 设置为指定的或者默认的原因值。

参数
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-abortsignal-abort① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


AbortSignal.onabort - Web API 接口参考

AbortSignal.onabort
===================

当事件关联的请求被中止时，触发 `AbortSignal` 接口的 **`abort`** 事件，例如，调用 `AbortController.abort()`。

语法
--

在 `addEventListener()` 等方法中使用事件名称，或者设置一个事件处理器属性。

```js
addEventListener("abort", (event) => {});
onabort = (event) => {};
```

事件类型
----

`Event` 泛型，无任何额外的属性。

示例
--

在下面例子中，我们将创建一个新的 `AbortController` 对象，并获取它的 `AbortSignal`（在 `signal` 属性中可用）。稍后，我们使用事件处理器属性检查信号是否被中止。

你可以使用 `addEventListener` 方法来检测 `abort` 事件：

```js
const controller = new AbortController();
const signal = controller.signal;

signal.addEventListener("abort", () => {
  console.log("Request aborted");
});
```

或者使用 `onabort` 事件处理器属性：

```js
const controller = new AbortController();
const signal = controller.signal;
signal.onabort = () => {
  console.log("Request aborted");
};
```

规范
--

| Specification |
| --- |
| DOM Standard # eventdef-abortsignal-abort |
| DOM Standard # abortsignal-onabort |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


AbortSignal.aborted - Web API 接口参考

AbortSignal.aborted
===================

**`aborted`** 是一个只读属性，它返回一个 `Boolean` (en-US) 表示与之通信的请求是否被终止（`true`）或未终止（`false`）

### 值

`true`（中止）或 `false`

示例
--

在下面的代码段中，我们创建了一个新的 `AbortController` 对象，并获取它的 `AbortSignal`（位于 `signal` 属性中）。然后我们用 `aborted` 属性检查这个信号是否已被中止，并把相应的日志发送给控制台。

```js
const controller = new AbortController();
const signal = controller.signal;

// …

if (signal.aborted) {
  console.log("Request has been aborted");
} else {
  console.log("Request not aborted");
}
```

规格
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-abortsignal-aborted① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* Fetch API


AbortSignal.throwIfAborted() - Web API 接口参考

AbortSignal.throwIfAborted()
============================

如果 signal 已经被中止，则 **`throwIfAborted()`** 方法抛出中止的 `reason`；否则它什么也不做。

需要支持中止的 API 可以接收一个 `AbortSignal` 对象并且使用 `throwIfAborted()` 来测试并且当 `abort` 事件发出信号时抛出。

该方法也可以用来中止代码中特定的操作，而不是传递给接收信号的函数。

语法
--

```js
throwIfAborted()
```

### 参数

无。

### 返回值

无（`undefined`）。

示例
--

以下示例来自规范。

### 中止一个轮询操作

该示例展示了你如何使用 `throwIfAborted()` 去中止一个轮询的操作。

考虑一个异步函数 `waitForCondition()`，在调用时传入另一个异步函数 `func`、目标值 `targetValue` 和 `AbortSignal`。该函数在循环中将 `func` 的结果和 `targetValue` 进行比较，当它们匹配时返回。

```js
async function waitForCondition(func, targetValue, { signal } = {}) {
  while (true) {
    signal?.throwIfAborted();

    const result = await func();
    if (result === targetValue) {
      return;
    }
  }
}
```

在每个循环的迭代中，如果操作已经被中止（否则什么也不做）我们使用 `throwIfAborted()` 来抛出 signal 中止的 `reason`。如果信号被中止，它将导致 `waitForCondition()` promise 被拒绝。

### 实现可中止的 API

需要支持中止的 API 可以接收一个 `AbortSignal` 对象，并在其需要时使用其状态去触发中止 signal 的操作。

一个基于 `Promise` 的 API 应该通过使用 `AbortSignal` abort `reason` 拒绝任何未敲定的 promise 来响应中止信号。例如，考虑以下 `myCoolPromiseAPI`，它接收一个信号并且返回一个 promise。如果 signal 已经中止或者检测到中止事件，则 promise 将被立刻拒绝。否则它将正常返回并且兑现。

```js
function myCoolPromiseAPI(/\* … ,\*/ { signal }) {
  return new Promise((resolve, reject) => {
    // If the signal is already aborted, immediately throw in order to reject the promise.
    if (signal.aborted) {
      reject(signal.reason);
    }

    // Perform the main purpose of the API
    // Call resolve(result) when done.

    // Watch for 'abort' signals
    signal.addEventListener("abort", () => {
      // Stop the main operation
      // Reject the promise wth the abort reason.
      reject(signal.reason);
    });
  });
}
```

然后使用 API，如下所示。请注意，调用 `AbortController.abort()` 以中止操作。

```js
const controller = new AbortController();
const signal = controller.signal;

startSpinner();

myCoolPromiseAPI({ /\* … ,\*/ signal })
  .then((result) => {})
  .catch((err) => {
    if (err.name === "AbortError") return;
    showUserErrorMessage();
  })
  .then(() => stopSpinner());

controller.abort();
```

不返回 promise 的 API 可能会以类似的方式做出反应。在某些情况下，保留信号可能是有意义的。

规范
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-abortsignal-throwifaborted① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* Fetch API


RTCTrackEvent() - Web API 接口参考

RTCTrackEvent()
===============

**`RTCTrackEvent()`** 构造函数创建并返回一个新的 `RTCTrackEvent` (en-US) 对象，用于描述已经被添加到 `RTCPeerConnection` 的轨道。

通常，你不需要使用这个构造函数，例如 `RTCTrackEvent` 这样的对象会由 WebRTC 创建，并根据需要传递到 `RTCPeerConnector` 的 `ontrack` 事件处理器上。

语法
--
```

new RTCTrackEvent(type, options)
```

### 参数

`type`

一个表示事件名称的字符串。大小写敏感，且浏览器始终将其设置为 `track`。

`options`

一个对象，*除了 `Event()` 中定义的对象*，还可以使用以下属性：

`receiver`

一个 `RTCRtpReceiver` (en-US)，用于接收轨道的媒体。

`streams` 可选

 一个 `MediaStream` 对象的数组，表示构成事件对应的轨道的流。它默认为一个空数组。
 It defaults to an empty array.

`track`

一个与事件关联的 `MediaStreamTrack`。

`transceiver`

一个与事件关联的 `RTCRtpTransceiver` (en-US)。

### 返回值

一个新的 `RTCTrackEvent` (en-US)，用于描述已经被添加到 `RTCPeerConnection` 的轨道。

规范
--

| Specification |
| --- |
| WebRTC: Real-Time Communication in Browsers # dom-rtctrackevent-constructor |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


使用 Service Worker - Web API 接口参考

使用 Service Worker
=================

本文是关于使用 service worker 的教程，包括讲解 service worker 的基本架构、怎么注册 service worker、新的 service worker 的安装以及激活的过程、怎么更新 service worker 还有它的缓存控制和自定义响应，这一切都在一个简单的离线的应用程序中。

Service worker 的背景
------------------

有一个困扰 web 用户多年的难题——丢失网络连接。即使是世界上最好的 web app，如果下载不了它，也是非常糟糕的体验。如今虽然已经有各种尝试来创造技术去尝试着解决这个问题，并且其中一些问题已经被解决。但是，最重要的问题是，仍然没有一个好的统筹机制对资源缓存和自定义的网络请求进行控制。

Service worker 修复了这个问题。使用 service worker，你可以将 app 设置为首先使用缓存资源，从而即使在离线状态，也可以提供默认的体验，然后从网络获取更多数据（通常称为“离线优先”）。这已经在原生 app 中可用，这是经常选择原生 app，而不是选择 web app 的主要原因之一。

service worker 的功能类似于代理服务器，允许你去修改请求和响应，将其替换成来自其自身缓存的项目。

使用 service worker 的准备工作
-----------------------

service worker 在现代浏览器中默认开启。要使用 service worker 运行代码，你需要通过 HTTPS 提供你的代码——出于安全原因，Service worker 仅限在 HTTPS 上运行。支持 HTTPS 的服务器是必要的。为了托管实验代码，你可以使用 Github、Netlify、Vercel 等服务。为了促进本地开发，浏览器也认为 `localhost` 是一个安全的来源。

基本步骤
----

通常遵循以下基本步骤来使用 service worker：

1. 获取 service worker 代码，然后使用 `serviceWorkerContainer.register()` 来注册。如果成功，service worker 将在 `ServiceWorkerGlobalScope` 中执行；这本质上是一种特殊的上下文，在主脚本执行线程之外运行，没有访问 DOM 的权限。Service Worker 现在已为处理事件做好准备。
2. 安装完成。`install` 事件始终是发送给 service worker 的第一个事件（这可用于启动填充 IndexedDB 和缓存站点资源的过程）。在此步骤期间，应用程序正在为离线可用做准备。
3. 当 `install` 程序处理完成时，service worker 被视为已安装。此时，service worker 的先前版本可能处于激活的状态并控制着打开的页面。由于我们不希望同一 service worker 的两个不同版本同时运行，因此新版本尚未激活。
4. 一旦 service worker 的旧版本控制的页面都已关闭，就可以安全地停用旧版本，并且新安装的 service worker 将收到 `activate` 事件。`activate` 的主要用途是去清理 service worker 之前版本使用的资源。新的 service worker 可以调用 `skipWaiting()` 要求立即激活，而无需要求打开的页面关闭。然后，新的 service worker 将立即收到 `activate` 事件，并将接管任何打开的页面。
5. 激活后，service worker 将立即控制页面，但是只会控制那些在 `register()` 成功后打开的页面。换句话说，文档必须重新加载才能真正的受到控制，因为文档在有或者没有 service worker 的情况下开始存在，并在其生命周期内维护它。为了覆盖次默认行为并在页面打开的情况下，service worker 可以调用 `clients.claim()` 方法。
6. 每当获取新版本的 service worker 时，都会再次发生此循环，并在新版本的激活期间清理上一个版本的残留。

![](/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers/sw-lifecycle.svg)

以下是可用的 service worker 事件的摘要：

* `install` (en-US)
* `activate` (en-US)
* `message` (en-US)
* 功能性事件
    + `fetch` (en-US)
    + `sync` (en-US)
    + `push` (en-US)
演示
--

为了展示注册和安装 service worker 的基础知识，我们已经创建了一个名为简单 service worker 的演示，这是一个乐高的星球大战图像库。它使用 promise 驱动的函数从 JSON 对象读取图像数据，并使用 `fetch()` 加载图像，然后将图像显示在页面的下一行。我们暂时让它保持不变。它同时也注册、安装和激活 service worker。

![The words Star Wars followed by an image of a Lego version of the Darth Vader character](/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers/demo-screenshot.png)

你也可以在 GitHub 上查看源代码以及简单 service worker 的在线演示。

### 注册你的 worker

在我们 app 的 JavaScript 文件（`app.js`）的第一个代码块中如下所示。这是我们使用 service worker 的入口点。

```js
const registerServiceWorker = async () => {
  if ("serviceWorker" in navigator) {
    try {
      const registration = await navigator.serviceWorker.register("/sw.js", {
        scope: "/",
      });
      if (registration.installing) {
        console.log("正在安装 Service worker");
      } else if (registration.waiting) {
        console.log("已安装 Service worker installed");
      } else if (registration.active) {
        console.log("激活 Service worker");
      }
    } catch (error) {
      console.error(`注册失败：${error}`);
    }
  }
};

// …

registerServiceWorker();
```

1. `if` 代码块进行特性检测测试，以确保在尝试注册 service worker 之前，该特性是被支持的。
2. 接着，我们使用 `ServiceWorkerContainer.register()` 函数来注册站点的 service worker。service worker 代码只是一个驻留在我们的 app 内的一个 JavaScript 文件（注意，这个文件的 URL 是相对于源（origin）的，而不是相对于引用它的那个 JS 文件）。
3. `scope` 参数是可选的，并且可以用来指定你想要 service worker 控制的子作用域。在这个例子中，我们指定了 `'/'`，其表示 app 的源（origin）下的所有内容。如果你留空的话，它的默认值也是这个，但是我们在这里指定它是为了更明确的阐述我们的目的。

这就注册了一个 service worker，它工作在 worker 上下文，所以没有访问 DOM 的权限。

单个 service worker 可以控制很多页面。每个你的作用域（scope）里的页面加载完的时候，安装在页面的 service worker 就可以控制它。牢记你需要小心 service worker 脚本里的全局变量：每个页面不会有自己独有的 worker。

**备注：** 关于 service worker 一个很棒的事情就是，如果你像我们上面做的那样使用特性检测，发现浏览器并不支持 service worker，但是它还是可以正常地以预期的方式在线使用你的 app。

#### 为什么我的 service worker 注册失败了？

可能是如下的原因：

* 你没有在 HTTPS 下运行你的程序。
* service worker 文件的路径没有写对——需要相对于源（origin），而不是 app 的根目录。在我们的示例中，worker 是在 `https://mdn.github.io/sw-test/sw.js`，app 的根目录是 `https://mdn.github.io/sw-test/`。但是路径需要写成 `/sw.js`。
* 也不允许你的 app 指向不同源（origin）的 service worker。
* service worker 只能在 service worker 作用域内捕获客户端发出的请求。
* service worker 最大的作用域是 worker 所在的位置（换句话说，如果脚本 `sw.js` 位于 `/js/sw.js` 中，默认情况下它只能控制 `/js/` 下的 URL）。可以使用 `Service-Worker-Allowed` 标头指定 worker 的最大作用域列表。
* 在 Firefox 中，若用户处于无痕浏览模式、禁用了历史记录或者启用了在 Firefox 关闭时清除历史记录，Service Worker API 将被隐藏而无法使用。
* 在 Chrome 中，当启用“阻止所有 Cookie（不建议）”选项时，注册将会失败。
### 安装和激活：填充你的缓存

在你的 service worker 注册之后，浏览器会尝试为你的页面或站点安装并激活它。

`install` 事件会在注册成功完成之后触发。`install` 事件通常会这样用，将离线运行 app 产生的资源放置在浏览器离线缓存的空间。为了实现这个，我们使用了 Service Worker 的存储 API——`cache`——一个 service worker 上的全局对象，它使我们可以存储网络响应发来的资源，并且根据它们的请求来生成 key。这个 API 和浏览器的标准的缓存工作原理很相似，但它特定于你的域的。直到你清理它们之前，这些内容都会持久存在。

以下是我们的 service worker 如何处理 `install` 事件：

```js
const addResourcesToCache = async (resources) => {
  const cache = await caches.open("v1");
  await cache.addAll(resources);
};

self.addEventListener("install", (event) => {
  event.waitUntil(
    addResourcesToCache([
      "/",
      "/index.html",
      "/style.css",
      "/app.js",
      "/image-list.js",
      "/star-wars-logo.jpg",
      "/gallery/bountyHunters.jpg",
      "/gallery/myLittleVader.jpg",
      "/gallery/snowTroopers.jpg",
    ]),
  );
});
```

1. 这里我们新增了一个 `install` 事件监听器去监听 service worker（这里指的是 `self`），接着在事件上调用 `ExtendableEvent.waitUntil()` 方法——这会确保 Service Worker 不会在 `waitUntil()` 里面的代码执行完毕之前安装完成。
2. 在 `addResourcesToCache()` 内，我们使用了 `caches.open()` 方法来创建了叫做 `v1` 的新缓存，这将会是我们的站点资源缓存的第 1 个版本。然后我们会在创建的缓存示例中调用 `addAll()` 函数，它的参数采用一个 URL 数组，指向你想要缓存的所有资源。其中，URL 是相对于 worker 的 location (en-US)。
3. 如果 promise 被拒绝，安装就会失败，这个 worker 不会做任何事情。这也是可以的，因为你可以修复你的代码，在下次注册的时候再次进行尝试。
4. 当安装成功完成之后，service worker 就会激活。在你的 service worker 第一次完成安装/激活时，这并没有什么用。但是当 service worker 更新（稍后查看更新你的 service worker 部分）的时候，就不太一样了。

**备注：** Web Storage API（`localStorage`）跟 service worker 的 cache 工作原理十分类似，但是它是同步的，所以不允许在 service worker 中使用。

**备注：** 如果你需要的话，可以在 service worker 中使用 IndexedDB 来做数据存储。

### 自定义请求的响应

现在你已经将你的站点资源缓存了，你需要告诉 service worker 让它用这些缓存内容来做点什么。有了 `fetch` 事件，这是很容易做到的。

1. 每次获取 service worker 控制的资源时，都会触发 `fetch` 事件，这些资源包括了指定的作用域内的文档，和这些文档内引用的其他任何资源（比如 `index.html` 发起了一个跨源的请求来嵌入一个图片，这个也会通过 service worker）。
2. 你可以给 service worker 添加一个 `fetch` 的事件监听器，接着调用 event 上的 `respondWith()` 方法来劫持我们的 HTTP 响应，然后你用可以用自己的方法来更新它们。

```js
self.addEventListener("fetch", (event) => {
  event.respondWith(/\* 在这里放置自定义的内容 \*/);
});
```

3. 在任何情况下，我们会首先响应缓存的 URL 和网络请求的 URL 相匹配的资源：

```js
self.addEventListener("fetch", (event) => {
  event.respondWith(caches.match(event.request));
});
```

`caches.match(event.request)` 允许我们对网络请求里的每个资源与缓存里可获取的等效资源进行匹配，查看缓存中是否有相应的资源。该匹配通过 URL 和各种标头进行，就像正常的 HTTP 请求一样。

![Fetch 事件图示](/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers/sw-fetch.svg)

恢复失败的请求
-------

在 service worker 的缓存中存在相匹配的资源时，`caches.match(event.request)` 是非常棒的。但是如果没有匹配资源呢？如果我们不提供任何错误处理，promise 就会兑现 `undefined`，因而我们不会得到任何内容。

在测试缓存的响应后，我们可以退回到常规网络请求：

```js
const cacheFirst = async (request) => {
  const responseFromCache = await caches.match(request);
  if (responseFromCache) {
    return responseFromCache;
  }
  return fetch(request);
};

self.addEventListener("fetch", (event) => {
  event.respondWith(cacheFirst(event.request));
});
```

如果资源不存在缓存中，它们则会从网络中进行请求。

使用更复杂的策略，我们不仅可以从网络中请求资源，还可以将其保存到缓存中，以便稍后对该资源的请求也可以离线检索。这意味着，如果将额外的图像添加到星球大战图库中，我们的 app 可以自动抓取并缓存它们。以下片段实现了这样的策略：

```js
const putInCache = async (request, response) => {
  const cache = await caches.open("v1");
  await cache.put(request, response);
};

const cacheFirst = async (request) => {
  const responseFromCache = await caches.match(request);
  if (responseFromCache) {
    return responseFromCache;
  }
  const responseFromNetwork = await fetch(request);
  putInCache(request, responseFromNetwork.clone());
  return responseFromNetwork;
};

self.addEventListener("fetch", (event) => {
  event.respondWith(cacheFirst(event.request));
});
```

如果请求 URL 在缓存中不可用，我们将使用 `await fetch（request)` 从网络请求中请求资源。之后，我们将响应的克隆放入缓存。`putInCache()` 函数使用 `caches.open('v1')` 和 `cache.put()` 将资源增加到缓存中。它的原始响应会返回给浏览器以提供给调用它的页面。

克隆响应是必要的，因为请求和响应流仅可以读取一次。为了返回响应到浏览器，并将其放入缓存中，我们得克隆它。因此原始的资源会返回给浏览器，克隆的资源会发送到缓存。它们都只能被读取一次。

看起来有点奇怪的是，`putInCache()` 返回的 promise 并没有使用 await。但原因是，我们并不想要等到缓存被添加至缓存后再返回响应。

我们现在唯一的问题是当请求没有匹配到缓存中的任何资源，或网络不可用的时，我们的请求依然会失败。让我们提供一个默认的回退方案以便不管发生了什么，用户至少能得到些东西：

```js
const putInCache = async (request, response) => {
  const cache = await caches.open("v1");
  await cache.put(request, response);
};

const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
  // 首先，尝试从缓存中获取资源
  const responseFromCache = await caches.match(request);
  if (responseFromCache) {
    return responseFromCache;
  }

  // 然后尝试从网络中获取资源
  try {
    const responseFromNetwork = await fetch(request);
    // 响应可能会被使用
    // 我们需要将它的拷贝放入缓存
    // 然后再返回该响应
    putInCache(request, responseFromNetwork.clone());
    return responseFromNetwork;
  } catch (error) {
    const fallbackResponse = await caches.match(fallbackUrl);
    if (fallbackResponse) {
      return fallbackResponse;
    }
    // 当回落的响应也不可用时，
    // 我们便无能为力了，但我们始终需要
    // 返回 Response 对象
    return new Response("Network error happened", {
      status: 408,
      headers: { "Content-Type": "text/plain" },
    });
  }
};

self.addEventListener("fetch", (event) => {
  event.respondWith(
    cacheFirst({
      request: event.request,
      fallbackUrl: "/gallery/myLittleVader.jpg",
    }),
  );
});
```

我们选择了回落的图片，因为唯一的更新是对新图片的，它可能会失败，因为其他的所有内容都依赖于我们之前看到的 `install` 事件监听器中的安装过程。

Service Worker 导航预加载
--------------------

如果启用了导航预加载 (en-US)功能，其将在发出 fetch 请求后，立即开始下载资源，并同时激活 service worker。这确保了在导航到一个页面时，立即开始下载，而不是等到 service worker 被激活。这种延迟发生的次数相对较少，但是一旦发生就不可避免，而且可能很重要。

首先，必须在 service worker 激活期间使用 `registration.navigationPreload.enable()` (en-US) 来启用该功能：

```js
self.addEventListener("activate", (event) => {
  event.waitUntil(self.registration?.navigationPreload.enable());
});
```

然后使用 `event.preloadResponse` (en-US) 等待预加载的资源在 `fetch` 事件处理程序中完成下载。

继续前几节的示例，我们插入代码，以便在缓存检查后等待预加载的资源，如果失败，则再从网络中获取。

新流程是：

1. 检查缓存
2. 等待 `event.preloadResponse`，它作为 `preloadResponsePromise` 传递给 `cacheFirst()` 函数。如果返回结果，则缓存它。
3. 如果两者均没有结果，那么我们就从网络中获取。

```js
const addResourcesToCache = async (resources) => {
  const cache = await caches.open("v1");
  await cache.addAll(resources);
};

const putInCache = async (request, response) => {
  const cache = await caches.open("v1");
  await cache.put(request, response);
};

const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
  // 首先，尝试从缓存中获取资源
  const responseFromCache = await caches.match(request);
  if (responseFromCache) {
    return responseFromCache;
  }

  // 接下来，尝试使用缓存或预加载的响应
  const preloadResponse = await preloadResponsePromise;
  if (preloadResponse) {
    console.info("using preload response", preloadResponse);
    putInCache(request, preloadResponse.clone());
    return preloadResponse;
  }

  // 然后尝试从网络中获取资源
  try {
    const responseFromNetwork = await fetch(request);
    // 响应可能会被使用
    // 我们需要将它的拷贝放入缓存
    // 然后再返回该响应
    putInCache(request, responseFromNetwork.clone());
    return responseFromNetwork;
  } catch (error) {
    const fallbackResponse = await caches.match(fallbackUrl);
    if (fallbackResponse) {
      return fallbackResponse;
    }
    // 当回落的响应也不可用时，
    // 我们便无能为力了，但我们始终需要
    // 返回 Response 对象
    return new Response("Network error happened", {
      status: 408,
      headers: { "Content-Type": "text/plain" },
    });
  }
};

// 启用导航预加载
const enableNavigationPreload = async () => {
  if (self.registration.navigationPreload) {
    await self.registration.navigationPreload.enable();
  }
};

self.addEventListener("activate", (event) => {
  event.waitUntil(enableNavigationPreload());
});

self.addEventListener("install", (event) => {
  event.waitUntil(
    addResourcesToCache([
      "/",
      "/index.html",
      "/style.css",
      "/app.js",
      "/image-list.js",
      "/star-wars-logo.jpg",
      "/gallery/bountyHunters.jpg",
      "/gallery/myLittleVader.jpg",
      "/gallery/snowTroopers.jpg",
    ]),
  );
});

self.addEventListener("fetch", (event) => {
  event.respondWith(
    cacheFirst({
      request: event.request,
      preloadResponsePromise: event.preloadResponse,
      fallbackUrl: "/gallery/myLittleVader.jpg",
    }),
  );
});
```

注意，在此示例中，无论资源是“正常”下载还是预加载，我们都会下载和缓存相同的数据。相反，你可以选择在预加载时下载和缓存其他资源。请参阅 `NavigationPreloadManager` > 自定义响应 (en-US) 以了解详情。

更新你的 service worker
-------------------

如果你的 service worker 已经被安装，但是刷新页面时有一个新版本的可用，新版的 service worker 会在后台安装，但是仍然不会被激活。当不再有任何已加载的页面在使用旧版的 service worker 的时候，新版本才会激活。一旦再也没有这样的已加载的页面，新的 service worker 就会被激活。

**备注：** 可以通过使用 `Clients.claim()` 绕过这一点。

你想把你的新版的 service worker 里的 `install` 事件监听器改成下面这样（注意新的版本号）：

```js
const addResourcesToCache = async (resources) => {
  const cache = await caches.open("v2");
  await cache.addAll(resources);
};

self.addEventListener("install", (event) => {
  event.waitUntil(
    addResourcesToCache([
      "/",
      "/index.html",
      "/style.css",
      "/app.js",
      "/image-list.js",

      // …

      // 包含新版本需要的其他资源…
    ]),
  );
});
```

当安装发生的时候，前一个版本依然在响应请求。新的版本正在后台安装。我们调用了一个新的缓存 `v2`，所以前一个 `v1` 版本的缓存不会被扰乱。

当没有页面在使用之前的版本的时候，这个新的 service worker 就会激活并开始响应请求。

### 删除旧缓存

正如我们在最后一节看到的那样，当你更新 service worker 到一个新的版本，你将在它的 `install` 事件处理程序中创建一个新的缓存。在仍有由上一个 worker 的版本控制的打开的页面，你就需要同时保留这两个版本的缓存，因为之前的版本需要它缓存的版本。你可以使用 `activate` 事件从之前的缓存中移除数据。

传给 `waitUntil()` 的 promise 会阻塞其他的事件，直到它完成，因此你可以放心，当你在新的 service worker 中得到你的第一个 `fetch` 事件时，你的清理操作已经完成。

```js
const deleteCache = async (key) => {
  await caches.delete(key);
};

const deleteOldCaches = async () => {
  const cacheKeepList = ["v2"];
  const keyList = await caches.keys();
  const cachesToDelete = keyList.filter((key) => !cacheKeepList.includes(key));
  await Promise.all(cachesToDelete.map(deleteCache));
};

self.addEventListener("activate", (event) => {
  event.waitUntil(deleteOldCaches());
});
```

开发者工具
-----

* Chrome
* Firefox
    + Firefox 工具栏自定义选项中的“Forget about this site”按钮可用于清除 service worker 及其缓存。
* Edge
参见
--

* Promise
* 使用 web worker


PerformanceTiming.responseStart - Web API 接口参考

PerformanceTiming.responseStart
===============================

概要
--

**`PerformanceTiming.responseStart`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为浏览器从服务器、缓存或者本地资源接收到响应的第一个字节之时的 Unix 毫秒时间戳。

语法
--
```

time = performanceTiming.responseStart;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.domainLookupStart - Web API 接口参考

PerformanceTiming.domainLookupStart
===================================

概要
--

**`PerformanceTiming.domainLookupStart`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为域名开始解析之时的 Unix 毫秒时间戳。如果一个持久连接被使用，或者该信息已经被本地资源或者缓存存储，则该值等同于 `PerformanceTiming.fetchStart`。

语法
--
```

time = performanceTiming.domainLookupStart;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.loadEventStart - Web API 接口参考

PerformanceTiming.loadEventStart
================================

概要
--

**`PerformanceTiming.loadEventStart`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为 `load` 事件被现在的文档触发之时的 Unix 时间戳。如果这个事件没有被触发，则他返回 `0`。

语法
--
```

time = performanceTiming.loadEventStart;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.loadEventEnd - Web API 接口参考

PerformanceTiming.loadEventEnd
==============================

概要
--

**`PerformanceTiming.loadEventEnd`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为 `load` 事件处理程序被终止，加载事件已经完成之时的 Unix 毫秒时间戳。如果这个事件没有被触发，或者没能完成，则该值将会返回 `0`。

语法
--
```

time = performanceTiming.loadEventEnd;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.domLoading - Web API 接口参考

PerformanceTiming.domLoading
============================

概要
--

**`PerformanceTiming.domLoading`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为解析器开始工作，即 `Document.readyState` 改变为 `'loading'` 并且 `readystatechange` 事件被触发之时的 Unix 毫秒时间戳。

语法
--
```

time = performanceTiming.domLoading;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.navigationStart - Web API 接口参考

PerformanceTiming.navigationStart
=================================

概要
--

**`PerformanceTiming.navigationStart`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为紧接着在相同的浏览环境下卸载前一个文档结束之时的 Unix 毫秒时间戳。如果没有上一个文档，则它的值相当于 `PerformanceTiming.fetchStart`。

语法
--
```

time = performanceTiming.navigationStart;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.domContentLoadedEventEnd - Web API 接口参考

PerformanceTiming.domContentLoadedEventEnd
==========================================

**`PerformanceTiming.domContentLoadedEventEnd`** 为只读属性，返回一个无符号长整型数值（`unsigned long），以 UNIX 时间戳的形式表示一个时刻，这个时刻为所有需要尽早执行的脚本不管是否按顺序，都已经执行完毕。（译注：即 DOM Ready）`

Syntax
------
```

time = performanceTiming.domContentLoadedEventEnd;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* 所属的 `PerformanceTiming` 接口


PerformanceTiming.connectStart - Web API 接口参考

PerformanceTiming.connectStart
==============================

概要
--

**`PerformanceTiming.connectStart`** 是一个返回代表一个时刻的 `unsigned long long 型只读属性，`请求连接被发送到网络之时的 Unix 毫秒时间戳。如果传输层报告错误并且连接的建立重新开始，则把最后建立连接的开始时间作为该值。如果一个持久连接被使用，则该值与 `PerformanceTiming.fetchStart` 相同。

语法
--
```

time = performanceTiming.connectStart;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.requestStart - Web API 接口参考

PerformanceTiming.requestStart
==============================

概要
--

**`PerformanceTiming.requestStart`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为浏览器发送从服务器或者缓存获取实际文档的请求之时的 Unix 毫秒时间戳。如果传输层在请求开始之后发生错误并且连接被重新打开，则该属性将会被设定为新的请求的相应的值。

语法
--
```

time = performanceTiming.requestStart;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.domainLookupEnd - Web API 接口参考

PerformanceTiming.domainLookupEnd
=================================

概要
--

**`PerformanceTiming.domainLookupEnd`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为解析域名结束时的 Unix 毫秒时间戳。如果一个持久连接被使用，或者该信息已经被本地资源或者缓存存储，则该值等同于 `PerformanceTiming.fetchStart`。

语法
--
```

time = performanceTiming.domainLookupEnd;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参加
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.redirectStart - Web API 接口参考

PerformanceTiming.redirectStart
===============================

概要
--

**`PerformanceTiming.redirectStart`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为第一个 HTTP 的重定向开始的时刻的 Unix 毫秒时间戳。如果重定向没有发生，或者其中一个重定向非同源，则该值返回 `0`。

语法
--
```

time = performanceTiming.redirectStart;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.responseEnd - Web API 接口参考

PerformanceTiming.responseEnd
=============================

概要
--

**`PerformanceTiming.responseEnd`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为浏览器从服务器、缓存或者本地资源接收响应的最后一个字节或者连接被关闭之时的 Unix 毫秒时间戳。

语法
--
```

time = performanceTiming.responseEnd;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.unloadEventEnd - Web API 接口参考

PerformanceTiming.unloadEventEnd
================================

概要
--

**`PerformanceTiming.unloadEventEnd`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为`unload` 事件处理程序结束之时的 Unix 毫秒时间戳。如果没有上一个的文档，或者上一个文档或需要被跳转的页面的其中之一不同源，则该值返回 `0`。

语法
--
```

time = performanceTiming.unloadEventEnd;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.domContentLoadedEventStart - Web API 接口参考

PerformanceTiming.domContentLoadedEventStart
============================================

**`PerformanceTiming.domContentLoadedEventStart`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为解析器发出 `DOMContentLoaded` 事件之前，即所有的需要被运行的脚本已经被解析之时的 Unix 毫秒时间戳。

值
-

一个 `unsigned long long` 值。

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.domComplete - Web API 接口参考

PerformanceTiming.domComplete
=============================

概要
--

**`PerformanceTiming.domComplete`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为主文档的解析器结束工作，`Document.readyState` 变为 `'complete'且相当于` `readystatechange` 事件被触发时的 Unix 毫秒时间戳。

语法
--
```

time = performanceTiming.domComplete;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.redirectEnd - Web API 接口参考

PerformanceTiming.redirectEnd
=============================

概要
--

**`PerformanceTiming.redirectEnd`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为最后一次的 HTTP 重定向被完成且 HTTP 响应的最后一个字节被接收之时的 Unix 毫秒时间戳。如果没有发生重定向，或者其中一个重定向不同源，则该值返回 `0`。

语法
--
```

time = performanceTiming.redirectEnd;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `Performance` 接口。


PerformanceTiming.unloadEventStart - Web API 接口参考

PerformanceTiming.unloadEventStart
==================================

概要
--

**`PerformanceTiming.unloadEventStart`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为 `unload` 事件被触发之时的 Unix 毫秒时间戳。如果没有上一个文档，或者上一个文档或需要重定向的页面之一不同源，则该值返回 `0`。

语法
--

```js
time = performanceTiming.unloadEventStart;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.fetchStart - Web API 接口参考

PerformanceTiming.fetchStart
============================

概要
--

**`PerformanceTiming.fetchStart`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为浏览器已经准备好去使用 HTTP 请求抓取文档之时的 Unix 毫秒时间戳。这一时刻在检查应用的缓存之前。

语法
--
```

time = performanceTiming.fetchStart;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.connectEnd - Web API 接口参考

PerformanceTiming.connectEnd
============================

**`PerformanceTiming.connectEnd`** 这个只读属性返回一个无符号长整型，它以毫秒为单位，代表了网络链接建立的时间节点。如果传输层报告了错误或者链接又被重新建立，则采用最后一次链接建立的时间。如果链接是长久的，那么这个值等同于`PerformanceTiming.fetchStart`。链接被认为打开以所有的链接握手，SOCKS 认证结束为标志。

Syntax
------
```

time = performanceTiming.connectEnd;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* 参见 `PerformanceTiming` 接口。


PerformanceTiming.secureConnectionStart - Web API 接口参考

PerformanceTiming.secureConnectionStart
=======================================

概要
--

**`PerformanceTiming.secureConnectionStart`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为安全连接握手开始的时刻的 Unix 毫秒时间戳。如果只要你过的连接没有被请求，则它返回 0。

语法
--
```

time = performanceTiming.secureConnectionStart;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。


PerformanceTiming.domInteractive - Web API 接口参考

PerformanceTiming.domInteractive
================================

概要
--

**`PerformanceTiming.domInteractive`** 是一个返回代表一个时刻的 `unsigned long long` 型只读属性，为在主文档的解析器结束工作，即 `Document.readyState` 改变为 `'interactive'` 并且相当于 `readystatechange` 事件被触发之时的 Unix 毫秒时间戳。

这个属性被用于测量用户感受的加载网页的速度。然而，如果脚本被屏蔽发生，而不是被异步加载或者使用了自定义的 Web 字体，这里有一些警告可能会发生。在使用这个值作为网页加载用户体验的媒介时，请务必检查一下你是否处于以上的情况。

语法
--
```

time = performanceTiming.domInteractive;
```

规范
--

因为 Navigation Timing 规范已被弃用，此特性不再有望成为标准。请使用 `PerformanceNavigationTiming` 接口代替。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 它属于 `PerformanceTiming` 接口。
* 文章 "domInteractive: is it? really?" 解释了什么时候你可以使用这个属性作为加载网页的用户体验的代理。


FontFaceSet.check() - Web API 接口参考

FontFaceSet.check()
===================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

`FontFaceSet` 的`check()`方法会返回是否在给定的字体列表中的所有字体已经被加载并可用。

句法
--
```

bool = aFontFaceSet .check(font);
bool = aFontFaceSet .check(font，text);
```

### 返回

如果字体列表可用，则`Boolean`为 true

### 参数

* `font`：使用CSS 值语法的字体规范，例如“italic bold 16px Roboto”
* `text`：将字体面限制为 Unicode 范围至少包含文本中的一个字符。这不检查单个字形覆盖。
例子
--

```js
document.fonts.check("12px courier"); //如果字体快递可用在 12px，则返回 true

document.fonts.check("12px MyFont"，"ß"); 如果字体“MyFont”具有ß字符，则返回 true。
```

规范
--

| Specification |
| --- |
| CSS Font Loading Module Level 3 # dom-fontfaceset-check |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


MutationObserver.disconnect() - Web API 接口参考

MutationObserver.disconnect()
=============================

`MutationObserver` 的 **`disconnect()`** 方法告诉观察者停止观察变动。可以通过调用其 `observe()` 方法来重用观察者。

语法
--
```

mutationObserver.disconnect()
```

### 参数

无。

### 返回值

`undefined`。

**备注：** 所有已经检测到但是尚未向观察者报告的变动都会被丢弃。

使用说明
----

如果被观察的元素被从 DOM 中移除，然后被浏览器的垃圾回收机制释放，此 `MutationObserver` 将同样被删除。

示例
--

下面的示例创建了一个观察者，接着与之断开连接，让它可以重复使用。

```js
var targetNode = document.querySelector("#someElement");
var observerOptions = {
  childList: true,
  attributes: true,
};

var observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);

/\* some time later... \*/

observer.disconnect();
```

规范
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-mutationobserver-disconnect① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


MutationObserver.takeRecords() - Web API 接口参考

MutationObserver.takeRecords()
==============================

`MutationObserver` 的 **`takeRecords()`** 方法返回已检测到但尚未由观察者的回调函数处理的所有匹配 DOM 更改的列表，使变更队列保持为空。此方法最常见的使用场景是在断开观察者之前立即获取所有未处理的更改记录，以便在停止观察者时可以处理任何未处理的更改。

语法
--
```

mutationRecords = mutationObserver.takeRecords()
```

### 参数

无。

### 返回值

返回一个`MutationRecord` 对象列表，每个对象都描述了应用于 DOM 树某部分的一次改动。

**备注：** 调用 `takeRecords()` 后，已发生但未传递给回调的变更队列将保留为空。

示例
--

下面的示例展示了在断开观察者之前如何通过调用 `takeRecords()` 来处理任何未传递的 `MutationRecord`。

```js
var targetNode = document.querySelector("#someElement");
var observerOptions = {
  childList: true,
  attributes: true,
};

var observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);

/\* ...later, when it's time to stop observing... \*/

/\* handle any still-pending mutations \*/

var mutations = observer.takeRecords();

if (mutations) {
  callback(mutations);
}

observer.disconnect();
```

代码中第 12-17 行抓取了所有未处理的变更记录，然后调用回调，并将变更记录列表传递给回调，以保证所有变更记录都被处理。这是在调用 `disconnect()` 之前完成的，以便停止观察 DOM。

规范
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-mutationobserver-takerecords① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


MutationObserver.MutationObserver() - Web API 接口参考

MutationObserver.MutationObserver()
===================================

DOM 规范中的 `MutationObserver()` 构造函数——是 `MutationObserver` 接口内容的一部分——创建并返回一个新的观察器，它会在触发指定 DOM 事件时，调用指定的回调函数。MutationObserver 对 DOM 的观察不会立即启动；而必须先调用 `observe()` 方法来确定，要监听哪一部分的 DOM 以及要响应哪些更改。

语法
--
```

var observer = new MutationObserver(callback);
```

### 参数

`callback`

一个回调函数，每当被指定的节点或子树以及配置项有 DOM 变动时会被调用。回调函数拥有两个参数：一个是描述所有被触发改动的 `MutationRecord` 对象数组，另一个是调用该函数的 `MutationObserver` 对象。参见下方的示例了解更多细节

### 返回值

一个新的、包含监听 DOM 变化回调函数的 `MutationObserver` 对象。

示例
--

这个例子简单地创建了一个新的 `MutationObserver`，监视一个节点及其全部子节点树的添加、移除元素，以及任何属性变化的事件。

### 回调函数

```js
function callback(mutationList, observer) {
  mutationList.forEach((mutation) => {
    switch (mutation.type) {
      case "childList":
        /\* 从树上添加或移除一个或更多的子节点；参见 mutation.addedNodes 与
 mutation.removedNodes \*/
        break;
      case "attributes":
        /\* mutation.target 中某节点的一个属性值被更改；该属性名称在 mutation.attributeName 中，
 该属性之前的值为 mutation.oldValue \*/
        break;
    }
  });
}
```

调用 `observe()` 即可开始观察 DOM。当观察者 observer 发现匹配观察请求中指定的配置项的更改时，`callback()` 方法便会被调用。

使用 `mutation.type` (en-US) 获取发生的变动类别（无论是子节点的变动，还是节点属性的变动）。

### 创建并使用 observer

使用以下代码设置一个观察进程。

```js
var targetNode = document.querySelector("#someElement");
var observerOptions = {
  childList: true, // 观察目标子节点的变化，是否有添加或者删除
  attributes: true, // 观察属性变动
  subtree: true, // 观察后代节点，默认为 false
};

var observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);
```

使用 ID `someElement` 来获取目标节点树。`observerOptions` 中设定了观察者的选项，通过设定 `childList` 和 `attributes` 为 `true` 来获取所需信息。

当 observer 实例化时，指定 `callback()` 函数。之后指定目标节点与记录选项，我们开始观察使用 `observe()` 指定的 DOM 节点。

从现在开始直到调用 `disconnect()` ，每次以 `targetNode` 为根节点的 DOM 树添加或移除元素时，以及这些元素的任意属性改变时，*`callback()` 都会被调用。*

规范
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-mutationobserver-mutationobserver① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


MutationObserver.observe() - Web API 接口参考

MutationObserver.observe()
==========================

`MutationObserver` 的 **`observe()`** 方法配置了 `MutationObserver` 对象的回调方法以开始接收与给定选项匹配的 DOM 变化的通知。根据配置，观察者会观察 DOM 树中的单个 `Node`，也可能会观察被指定节点的部分或者所有的子孙节点。

要停止 `MutationObserver`（以便不再触发它的回调方法），需要调用 `MutationObserver.disconnect()` 方法。

语法
--
```

mutationObserver.observe(target[, options])
```

### 参数

`target`

DOM 树中的一个要观察变化的 DOM `Node` (可能是一个 `Element`)，或者是被观察的子节点树的根节点。

`options`

此对象的配置项描述了 DOM 的哪些变化应该报告给 `MutationObserver` 的 `callback`。当调用 `observe()` 时，`childList`、`attributes` 和 `characterData` 中，必须有一个参数为 `true`。否则会抛出 `TypeError` 异常。

`options` 的属性如下：

`subtree` 可选

当为 `true` 时，将会监听以 `target` 为根节点的整个子树。包括子树中所有节点的属性，而不仅仅是针对 `target`。默认值为 `false`。

`childList` 可选

当为 `true` 时，监听 `target` 节点中发生的节点的新增与删除（同时，如果 `subtree` 为 `true`，会针对整个子树生效）。默认值为 `false`。

`attributes` 可选

当为 `true` 时观察所有监听的节点属性值的变化。默认值为 `true`，当声明了 `attributeFilter` 或 `attributeOldValue`，默认值则为 `false`。

`attributeFilter` 可选

一个用于声明哪些属性名会被监听的数组。如果不声明该属性，所有属性的变化都将触发通知。

`attributeOldValue` 可选

当为 `true` 时，记录上一次被监听的节点的属性变化；可查阅监听属性值了解关于观察属性变化和属性值记录的详情。默认值为 `false`。

`characterData` 可选

当为 `true` 时，监听声明的 `target` 节点上所有字符的变化。默认值为 `true`，如果声明了 `characterDataOldValue`，默认值则为 `false`

`characterDataOldValue` 可选

当为 `true` 时，记录前一个被监听的节点中发生的文本变化。默认值为 `false`

### 返回值

`undefined`。

### 异常

`TypeError`

以下任一情况都会抛出异常：

* 配置选项使得实际上不会监视任何内容（例如，如果 `MutationObserverInit.childList`，`MutationObserverInit.attributes` (en-US) 和 `MutationObserverInit.characterData` (en-US) 都为 `false`）。
* `attributes` 选项为 `false`（表示不监视属性更改）但是`attributeOldValue` 为 `true` 并且/或者 `attributeFilter` 配置存在。
* `characterDataOldValue` 选项为 `true` 但是 `MutationObserverInit.characterData` (en-US) 为 `false`（表示不跟踪字符更改）。

使用说明
----

### 复用 MutationObserver

你可以多次调用同一个 `MutationObserver` 对象的 `observe()` 方法，来观察 DOM 树中不同部分的变化，和/或不同类型的变化。有一些需要注意的注意事项：

* 如果在已经被同一 `MutationObserver` 观察的节点上调用 `observe()` 方法，则在激活新观察者之前，所有现有观察者将自动从所有正在观察的目标中移除。
* 如果同一个 `MutationObserver` 还没有作用在 target 上，则保留现有观察者并添加新观察者。
### 当节点断开连接时继续观察节点

`MutationObserver` 旨在让你能够随着时间的推移观察所需的节点集，即使这些节点之间的直接连接被切断。如果你开始观察节点的子树，并且该子树的一部分被分离并移动到 DOM 中的其他位置，你将继续观察分离的节点段，接收与节点从原始子树分离之前相同的回调。

换句话说，在你收到有关节点从被观察子树中拆分的通知之前，你将收到有关该拆分子树及其节点的更改的通知。这可以防止你丢失在切断连接之后以及在你有机会专门开始观察已移动的节点或子树之前发生的变化。

这意味着理论上如果你跟踪描述发生的变化的 `MutationRecord` 对象，你就可以“撤销”这些改动，将 DOM 恢复到初始状态。

示例
--

在此例子中，将为你演示如何在实例 `MutationObserver` 中调用 **`observe()`** 方法，一旦设置后，会传给他一个目标元素和一个 `MutationObserverInit` 配置对象。

```js
// 得到要观察的元素
var elementToObserve = document.querySelector("#targetElementId");

// 创建一个叫 `observer` 的新 `MutationObserver` 实例，
// 并将回调函数传给它
var observer = new MutationObserver(function () {
  console.log("callback that runs when observer is triggered");
});

// 在 MutationObserver 实例上调用 `observe` 方法，
// 并将要观察的元素与选项传给此方法
observer.observe(elementToObserve, { subtree: true, childList: true });
```

规范
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-mutationobserver-observe② |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


IDBCursor.direction - Web API 接口参考

IDBCursor.direction
===================

`IDBCursor` 的方向属性是一个 `DOMString` ，表示游标遍历的方向， (比如可以通过 `IDBObjectStore.openCursor` 设置). 查看下文中取值章节获取可取值。

语法
--

```js
cursor.direction;
```

### 取值

用一个字符串 (defined by the `IDBCursorDirection` enum) 表示游标的遍历方向。相关取值如下表所示：

| Value | Description |
| --- | --- |
| `next` | 从数据源开始位置遍历 |
| `nextunique` | 从数据源开始遍历；当取值有重复时，只获取一次。 |
| `prev` | 从数据源的最后位置位置开取值 |
| `prevunique` | 从数据源的最后位置开始取值，只获取一次。 |

例子
--

在这个简单的例子中，我们首先创建一个事物对象，返回一个对象仓库 (store), 然后使用邮编遍历整个数据仓库。在每次迭代中我们记录了游标的方向，例如 prev（倒序遍历）

```html
prev
```

**备注：** 我们不能改变游标的取值，因为这是个只读属性；应该在`IDBObjectStore.openCursor`方法调用的第二个参数指定游标遍历的方向；

使用游标遍历数据时，可以不需要我们指定在特定字段选择数据；我们可以直接获取所有数据，同时在每次循环迭代过程当中，我们可以通过 cursor.value.foo 获取数据，如下是一个完整的游标遍历数据的例子； IDBCursor example (view example live).

```js
function backwards() {
  list.innerHTML = "";
  var transaction = db.transaction(["rushAlbumList"], "readonly");
  var objectStore = transaction.objectStore("rushAlbumList");

  objectStore.openCursor(null, "prev").onsuccess = function (event) {
    var cursor = event.target.result;
    if (cursor) {
      var listItem = document.createElement("li");
      listItem.innerHTML =
        "<strong>" +
        cursor.value.albumTitle +
        "</strong>, " +
        cursor.value.year;
      list.appendChild(listItem);

      console.log(cursor.direction);
      cursor.continue();
    } else {
      console.log("Entries displayed backwards.");
    }
  };
}
```

Specifications
--------------

| Specification |
| --- |
| Indexed Database API 3.0 # ref-for-dom-idbcursor-direction① |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参考资料
----

* Using IndexedDB
* Starting transactions: `IDBDatabase`
* Using transactions: `IDBTransaction`
* Setting a range of keys: `IDBKeyRange`
* Retrieving and making changes to your data: `IDBObjectStore`
* Using cursors: `IDBCursor`
* Reference example: To-do Notifications (view example live.)