Lock：mode 属性 - Web API 接口参考

Lock：mode 属性
============

**安全上下文:** 此项功能仅在一些支持的浏览器的安全上下文（HTTPS）中可用。

`Lock` 接口的 **`mode`** 只读属性返回请求锁时传递给 `LockManager.request()` 的访问模式。该模式可以是 `"exclusive"`（默认值）或 `"shared"`。

**备注：** 此特性在 Web Worker 中可用

值
-

`"exclusive"` 或 `"shared"` 之一。

示例
--

以下示例显示如何在对 `LockManager.request()` 的调用中传递 mode 属性。其中 `LockManager` 是 `navigator.locks` 返回的对象。

```js
// 应显示 "exclusive"（默认值）
navigator.locks.request("my\_resource", show_lock_properties);

// 应显示 "exclusive"
navigator.locks.request(
  "my\_resource",
  { mode: "exclusive" },
  show_lock_properties,
);

// 应显示 "shared"
navigator.locks.request(
  "my\_resource",
  { mode: "shared" },
  show_lock_properties,
);

function show\_lock\_properties(lock) {
  console.log(`锁的名称是：${lock.name}`);
  console.log(`锁的模式是：${lock.mode}`);
}
```

规范
--

| Specification |
| --- |
| Web Locks API # dom-lock-mode |

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

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


Lock：name 属性 - Web API 接口参考

Lock：name 属性
============

**安全上下文:** 此项功能仅在一些支持的浏览器的安全上下文（HTTPS）中可用。

`Lock` 接口的 **`name`** 只读属性返回请求锁时传递给 `LockManager.request` 选择的*名称*。

当请求锁时，锁的名称通过脚本传递。该名称由开发人员选择来表示一个抽象资源，该资源的使用在同源的多个标签页、worker 或其他代码之间进行协调。例如，如果 Web 应用程序同时只能有一个标签页进行网络资源与脱机数据库的同步，则它可以使用诸如 `"net_db_sync"` 之类的锁名称。

**备注：** 此特性在 Web Worker 中可用

值
-

一个字符串。

示例
--

以下示例显示了如何在对 `LockManager.request()` 的调用中传递 name 属性。其中 `LockManager` 是 `navigator.locks` 返回的对象。

```js
navigator.locks.request("net\_db\_sync", show_lock_properties);

function show\_lock\_properties(lock) {
  console.log(`锁的名称是：${lock.name}`);
  console.log(`锁的模式是：${lock.mode}`);
}
```

规范
--

| Specification |
| --- |
| Web Locks API # dom-lock-name |

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

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


PageTransitionEvent.persisted - Web API 接口参考

PageTransitionEvent.persisted
=============================

只读属性 **`persisted`** 代表一个页面是否从缓存中加载的

Syntax
------

```js
window.addEventListener("pageshow", function (event) {
  if (event.persisted) {
    console.log("Page was loaded from cache.");
  }
});
```

### Value

A `Boolean`.

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

| Specification |
| --- |
| HTML Standard # dom-pagetransitionevent-persisted-dev |

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

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


BeforeInstallPromptEvent.prompt() - Web API 接口参考

BeforeInstallPromptEvent.prompt()
=================================

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

`BeforeInstallPromptEvent` 接口的 **`prompt()`** 方法允许一个开发人员在自己选择的一个时间显示安装提示。

句法
--
```

BeforeInstallPromptEvent.prompt()
```

### 参数

无

### 返回

一个空的 `Promise`.

范例
--
```

let isTooSoon = true;
window.addEventListener("beforeinstallprompt", function(e) {
  if (isTooSoon) {
    e.preventDefault(); // Prevents prompt display
    // Prompt later instead:
    setTimeout(function() {
      isTooSoon = false;
      e.prompt(); // Throws if called more than once or default not prevented
    }, 10000);
  }

  // The event was re-dispatched in response to our request
  // ...
});
```

规范
--

This method is not part of a specification.

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

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


Geolocation.clearWatch() - Web API 接口参考

Geolocation.clearWatch()
========================

**`Geolocation.clearWatch()`** 这个方法用于注销之前使用 `Geolocation.watchPosition()` 注册的位置监听器或错误监听器。

语法
--
```

navigator.geolocation.clearWatch(id);
```

### 参数

`id`

希望移除的监听器所对应的 `Geolocation.watchPosition()` 返回的 ID 值。

示例
--

```js
var id, target, option;

function success(pos) {
  var crd = pos.coords;

  if (target.latitude === crd.latitude && target.longitude === crd.longitude) {
    console.log("Congratulation, you reach the target");
    navigator.geolocation.clearWatch(id);
  }
}

function error(err) {
  console.warn("ERROR(" + err.code + "): " + err.message);
}

target = {
  latitude: 0,
  longitude: 0,
};

options = {
  enableHighAccuracy: false,
  timeout: 5000,
  maximumAge: 0,
};

id = navigator.geolocation.watchPosition(success, error, options);
```

规范
--

| Specification |
| --- |
| Geolocation API # clearwatch-method |

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

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

参见
--

* 使用地理位置定位
* `Geolocation`
* `Geolocation.watchPosition()`
* `Geolocation.getCurrentPosition()`


Geolocation.watchPosition() - Web API 接口参考

Geolocation.watchPosition()
===========================

**`Geolocation.watchPosition()`** 用于注册监听器，在设备的地理位置发生改变的时候自动被调用。也可以选择特定的错误处理函数。

该方法会返回一个 ID，如要取消监听可以通过 `Geolocation.clearWatch()` 传入该 ID 实现取消的目的。

语法
--

```js
watchPosition(success)
watchPosition(success, error)
watchPosition(success, error, options)
```

### 参数

*success*

成功时候的回调函数，同时传入一个 `Position` 对象当作参数。

*error* 可选

失败时候的回调函数，可选，会传入一个 `PositionError` 对象当作参数。

*options* 可选

一个可选的 `PositionOptions` 对象。

示例
--

```js
var id, target, options;

function success(pos) {
  var crd = pos.coords;

  if (target.latitude === crd.latitude && target.longitude === crd.longitude) {
    console.log("Congratulations, you reached the target");
    navigator.geolocation.clearWatch(id);
  }
}

function error(err) {
  console.warn("ERROR(" + err.code + "): " + err.message);
}

target = {
  latitude: 0,
  longitude: 0,
};

options = {
  enableHighAccuracy: false,
  timeout: 5000,
  maximumAge: 0,
};

id = navigator.geolocation.watchPosition(success, error, options);
```

规范
--

| Specification |
| --- |
| Geolocation API # watchposition-method |

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

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

参见
--

* 使用地理位置定位
* 该方法属于 `Geolocation`，可以通过 `NavigatorGeolocation.geolocation` 访问。
* 取消监听的方法： `Geolocation.clearWatch()`
* 另一个类似的方法： `Geolocation.getCurrentPosition()`


Geolocation.getCurrentPosition() - Web API 接口参考

Geolocation.getCurrentPosition()
================================

**`Geolocation.getCurrentPosition()`** 方法用来获取设备当前位置。

语法
--
```

navigator.geolocation.getCurrentPosition(success, error, options)
```

### 参数

*success*

成功得到位置信息时的回调函数，使用`Position` 对象作为唯一的参数。

*error* 可选

获取位置信息失败时的回调函数，使用 `PositionError` 对象作为唯一的参数，这是一个可选项。

*options* 可选

一个可选的`PositionOptions` 对象。

实例
--

```js
var options = {
  enableHighAccuracy: true,
  timeout: 5000,
  maximumAge: 0,
};

function success(pos) {
  var crd = pos.coords;

  console.log("Your current position is:");
  console.log("Latitude : " + crd.latitude);
  console.log("Longitude: " + crd.longitude);
  console.log("More or less " + crd.accuracy + " meters.");
}

function error(err) {
  console.warn("ERROR(" + err.code + "): " + err.message);
}

navigator.geolocation.getCurrentPosition(success, error, options);
```

标准
--

| Specification |
| --- |
| Geolocation API # getcurrentposition-method |

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

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

请参考
---

* Using geolocation
* `Navigator.geolocation`


XMLHttpRequest 中的 HTML - Web API 接口参考

XMLHttpRequest 中的 HTML
======================

W3C `XMLHttpRequest` 规范为 `XMLHttpRequest` 添加 HTML 解析功能，此前它仅支持 XML 解析。该功能允许 Web 应用程序使用 `XMLHttpRequest` 获得已解析的 DOM 形式的 HTML 资源。

要了解如何使用 `XMLHttpRequest`，请参阅使用 XMLHttpRequest。

局限性
---

为了阻止同步使用 `XMLHttpRequest`，HTML 支持在同步模式下是不可用的。另外，只有当 `responseType` 属性被设置为 `"document"` 时，才会有 HTML 支持。这一限制避免了当遗留代码在默认模式下使用 `XMLHttpRequest` 来检索 `responseText`，对 `text/html` 资源进行无用的解析而浪费时间。同时，这个限制也避免了遗留代码的问题，这些代码假定 `responseXML` 对于 HTTP 错误页面（通常有一个 `text/html` 响应体）是 `null`。

用法
--

使用 `XMLHttpRequest` 检索作为 DOM 的 HTML 资源，就像使用 `XMLHttpRequest` 检索作为 DOM 的 XML 资源一样，只是你不能使用同步模式，你必须在调用 `open()` 之后，`send()` 之前，将字符串 `"document"` 分配给 `responseType` 对象的属性，来明确请求一个文档。

```js
const xhr = new XMLHttpRequest();
xhr.onload = () => {
  console.log(xhr.responseXML.title);
};
xhr.open("GET", "file.html");
xhr.responseType = "document";
xhr.send();
```

特性检测
----

### 方法 1

该方法依赖于功能的“强制异步”性质。当你试图设置 `XMLHttpRequest` 对象的 `responseType` 时，它以“同步”方式打开。这在实现该功能的浏览器中会出现错误，而在其他浏览器中则可以正常工作。

```js
function HTMLinXHR() {
  if (!window.XMLHttpRequest) {
    return false;
  }
  const req = new window.XMLHttpRequest();
  req.open("GET", window.location.href, false);
  try {
    req.responseType = "document";
  } catch (e) {
    return true;
  }
  return false;
}
```

在 JSFiddle 中查看

这种方法是同步的，不依赖外部资源，尽管它可能不像下面描述的方法 2 那样可靠，因为它不检查实际功能，而是检查该功能的指示。

### 方法 2

在 `XMLHttpRequest` 中准确检测一个浏览器是否支持 HTML 解析有两个挑战。首先，检测结果是以异步方式获得的，因为只有在异步模式下才有 HTML 支持。其次，你必须通过 HTTP 实际获取一个测试文档，因为用 `data:` URL 进行测试，最终会同时测试 `data:` URL 支持。

因此，为了检测 HTML 支持，服务器上需要一个测试 HTML 文件。这个测试文件很小，其 XML 格式不是很完整：

```js
<title>&&<</title>
```

如果文件名为 `detect.html`，以下功能可用于检测 HTML 解析支持：

```js
function detectHtmlInXhr(callback) {
  if (!window.XMLHttpRequest) {
    setTimeout(function () {
      callback(false);
    }, 0);

    return;
  }
  let done = false;
  const xhr = new window.XMLHttpRequest();
  xhr.onreadystatechange = () => {
    if (xhr.readyState === 4 && !done) {
      done = true;
      callback(
        !!(
          xhr.responseXML &&
          xhr.responseXML.title &&
          xhr.responseXML.title === "&&<"
        ),
      );
    }
  };
  xhr.onabort = xhr.onerror = () => {
    if (!done) {
      done = true;
      callback(false);
    }
  };
  try {
    xhr.open("GET", "detect.html");
    xhr.responseType = "document";
    xhr.send();
  } catch (e) {
    setTimeout(function () {
      if (!done) {
        done = true;
        callback(false);
      }
    }, 0);
  }
}
```

参数 `callback` 是一个函数，如果 HTML 解析是支持的，则将以 `true` 作为唯一参数被异步调用，如果不支持 HTML 解析，则为 `false`。

在 JSFiddle 中查看

字符编码
----

如果在 HTTP `Content-Type` 标头中声明了字符编码，则使用该字符编码。否则，如果存在字节顺序标记，则使用由字节顺序标记指示的编码。否则，如果存在 `<meta>` 元素声明文件的前 1024 个字节中的编码，则使用该编码。否则，文件被解码为 UTF-8。

规范
--

| Specification |
| --- |
| XMLHttpRequest Standard # interface-xmlhttprequest |

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

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

参见
--

* `XMLHttpRequest`
* 使用 XMLHttpRequest


FormData 对象的使用 - Web API 接口参考

FormData 对象的使用
==============

FormData 对象用以将数据编译成键值对，以便用`XMLHttpRequest`来发送数据。其主要用于发送表单数据，但亦可用于发送带键数据 (keyed data)，而独立于表单使用。如果表单`enctype`属性设为 multipart/form-data，则会使用表单的`submit()`方法来发送数据，从而，发送数据具有同样形式。

从零开始创建 FormData 对象
------------------

你可以自己创建一个`FormData`对象，然后调用它的`append()`方法来添加字段，像这样：

```js
var formData = new FormData();

formData.append("username", "Groucho");
formData.append("accountnum", 123456); //数字 123456 会被立即转换成字符串 "123456"

// HTML 文件类型 input，由用户选择
formData.append("userfile", fileInputElement.files[0]);

// JavaScript file-like 对象
var content = '<a id="a"><b id="b">hey!</b></a>'; // 新文件的正文
var blob = new Blob([content], { type: "text/xml" });

formData.append("webmasterfile", blob);

var request = new XMLHttpRequest();
request.open("POST", "http://foo.com/submitform.php");
request.send(formData);
```

**备注：** 字段 "userfile" 和 "webmasterfile" 都包含一个文件。字段 "accountnum" 是数字类型，它将被`FormData.append()` 方法转换成字符串（字段类型可以是 `Blob`、`File` 或者字符串：**如果它的字段类型不是 Blob 也不是 File，则会被转换成字符串类**）。

上面的示例创建了一个 `FormData` 实例，包含"username"、"accountnum"、"userfile" 和 "webmasterfile"四个字段，然后使用 `XMLHttpRequest` 的 `send()` 方法发送表单数据。字段 "webmasterfile" 是 `Blob` 类型。一个 **Blob** 对象表示一个不可变的，原始数据的类似文件对象。Blob 表示的数据不一定是一个 JavaScript 原生格式。 `File` 接口基于 Blob，继承 blob 功能并将其扩展为支持用户系统上的文件。你可以通过 `Blob()` 构造函数创建一个 Blob 对象。

通过 HTML 表单创建 FormData 对象
------------------------

想要构造一个包含 Form 表单数据的 FormData 对象，需要在创建 FormData 对象时指定表单的元素。

**备注：** FormData 将仅使用具有 name 属性的输入字段。

```js
var formData = new FormData(someFormElement);
```

示例：

```js
var formElement = document.querySelector("form");
var request = new XMLHttpRequest();
request.open("POST", "submitform.php");
request.send(new FormData(formElement));
```

你还可以在创建一个包含 Form 表单数据的 FormData 对象之后和发送请求之前，附加额外的数据到 FormData 对象里，像这样：

```js
var formElement = document.querySelector("form");
var formData = new FormData(formElement);
var request = new XMLHttpRequest();
request.open("POST", "submitform.php");
formData.append("serialnumber", serialNumber++);
request.send(formData);
```

这样你就可以在发送请求之前自由地附加不一定是用户编辑的字段到表单数据里。

使用 FormData 对象上传文件
------------------

你还可以使用 FormData 上传文件。使用的时候需要在表单中添加一个文件类型的 input：

```html
<form enctype="multipart/form-data" method="post" name="fileinfo">
  <label>Your email address:</label>
  <input
 type="email"
 autocomplete="on"
 autofocus
 name="userid"
 placeholder="email"
 required
 size="32"
 maxlength="64" /><br />
  <label>Custom file label:</label>
  <input type="text" name="filelabel" size="12" maxlength="32" /><br />
  <label>File to stash:</label>
  <input type="file" name="file" required />
  <input type="submit" value="Stash the file!" />
</form>
<div></div>
```

然后使用下面的代码发送请求：

```js
var form = document.forms.namedItem("fileinfo");
form.addEventListener(
  "submit",
  function (ev) {
    var oOutput = document.querySelector("div"),
      oData = new FormData(form);

    oData.append("CustomField", "This is some extra data");

    var oReq = new XMLHttpRequest();
    oReq.open("POST", "stash.php", true);
    oReq.onload = function (oEvent) {
      if (oReq.status == 200) {
        oOutput.innerHTML = "Uploaded!";
      } else {
        oOutput.innerHTML =
          "Error " +
          oReq.status +
          " occurred when trying to upload your file.<br />";
      }
    };

    oReq.send(oData);
    ev.preventDefault();
  },
  false,
);
```

**备注：** 如果 FormData 对象是通过表单创建的，则表单中指定的请求方式会被应用到方法 open() 中。

你还可以直接向 FormData 对象附加 File 或 Blob 类型的文件，如下所示：

```js
data.append("myfile", myBlob, "filename.txt");
```

使用 append() 方法时，可以通过第三个可选参数设置发送请求的头 `Content-Disposition` 指定文件名。如果不指定文件名（或者不支持该参数时），将使用名字“blob”。

如果你设置正确的配置项，你也可以通过 jQuery 来使用 FormData 对象：

```js
var fd = new FormData(document.querySelector("form"));
fd.append("CustomField", "This is some extra data");
$.ajax({
  url: "stash.php",
  type: "POST",
  data: fd,
  processData: false, // 不处理数据
  contentType: false, // 不设置内容类型
});
```

不使用 FormData 对象，通过 AJAX 提交表单和上传文件
---------------------------------

如果你想知道不使用 FormData 对象的情况下，如何序列化并通过 AJAX 提交表单，请点击这里。

参见
--

* 使用 XMLHttpRequest
* `HTMLFormElement`
* `Blob`
* 类型化数组


使用 XMLHttpRequest - Web API 接口参考

使用 XMLHttpRequest
=================

在该教程中，我们将使用`XMLHttpRequest` 来发送 HTTP 请求以实现网站和服务器之间的数据交换。`XMLHttpRequest`常见和晦涩的使用情况都将包含在例子中。

发送一个 HTTP 请求，需要创建一个 `XMLHttpRequest` 对象，打开一个 URL，最后发送请求。当所有这些事务完成后，该对象将会包含一些诸如响应主体或 HTTP status 的有用信息。

```js
function reqListener() {
  console.log(this.responseText);
}

var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();
```

请求类型
----

通过 `XMLHttpRequest` 生成的请求可以有两种方式来获取数据，异步模式或同步模式。请求的类型是由这个 `XMLHttpRequest` 对象的 open() 方法的第三个参数`async`的值决定的。如果该参数的值为 `false`，则该 `XMLHttpRequest`请求以同步模式进行，否则该过程将以异步模式完成。这两种类型请求的详细讨论和指南可以在同步和异步请求页找到。

**备注：** 由于对用户体验的负面影响，从 Gecko 30.0 版本开始，在主线程上的同步请求已经被弃用。

**备注：** `XMLHttpRequest` 构造函数并不仅限于 XML 文档。它之所以使用“XML”开头是因为在它诞生之时，原先用于异步数据交换的主要格式便是 XML。

处理响应
----

W3C 规范定义了 `XMLHttpRequest()` 对象的几种类型的响应属性。这些属性告诉客户端关于 `XMLHttpRequest` 返回状态的重要信息。一些处理非文本返回类型的用例，可能包含下面章节所描述的一些操作和分析。

### 分析并操作 responseXML 属性

如果你使用 `XMLHttpRequest` 来获得一个远程的 XML 文档的内容，`responseXML` 属性将会是一个由 XML 文档解析而来的 DOM 对象，这很难被操作和分析。这里有五种主要的分析 XML 文档的方式：

1. 使用 XPath 定位到文档的指定部分。
2. 手动解析和序列化 XML 为字符串或对象。
3. 使用 XMLSerializer 把 DOM 树序列化成字符串或文件。
4. 如果你预先知道 XML 文档的内容，你可以使用 RegExp。如果你用 `RegExp` 扫描时受到换行符的影响，你也许想要删除所有的换行符。然而，这种方法是"最后手段"，因为如果 XML 代码发生轻微变化，该方法将可能失败。

**备注：** 在 W3C XMLHttpRequest 规范中允许 HTML 通过 XMLHttpRequest.responseXML 属性进行解析。更多详细内容请阅读 HTML in XMLHttpRequest 。本条注意已在英文原文中更新。

**备注：** `XMLHttpRequest` 现在可以使用 `responseXML` 属性解释 HTML。请阅读 HTML in XMLHttpRequest 这篇文章了解相关用法。

### 解析和操作包含 HTML 文档的 responseText 属性

如果使用 `XMLHttpRequest` 从远端获取一个 HTML 页面，则所有 HTML 标记会以字符串的形式存放在 responseText 属性里，这样就使得操作和解析这些标记变得困难。解析这些 HTML 标记主要有三种方式：

1. 使用 `XMLHttpRequest.responseXML` 属性。
2. 将内容通过 `fragment.body.innerHTML` 注入到一个 文档片段 中，并遍历 DOM 中的片段。
3. 如果你预先知道 HTML 文档的内容，你可以使用 RegExp。如果你用 RegExp 扫描时受到换行符的影响，你也许想要删除所有的换行符。然而，这种方法是"最后手段"，因为如果 HTML 代码发生轻微变化，该方法将可能失败。
处理二进制数据
-------

尽管 `XMLHttpRequest` 一般用来发送和接收文本数据，但其实也可以发送和接收二进制内容。有许多经过良好测试的方法来强制使用 `XMLHttpRequest` 发送二进制数据。利用 `XMLHttpRequest` 对象的 `overrideMimeType()` 方法是一个解决方案，虽然它并不是一个标准方法。

```js
var oReq = new XMLHttpRequest();
oReq.open("GET", url);
// 以二进制字符串形式检索未处理的数据
oReq.overrideMimeType("text/plain; charset=x-user-defined");
/\* ... \*/
```

然而，自从 `responseType` 属性目前支持大量附加的内容类型后，已经出现了很多的现代技术，它们使得发送和接收二进制数据变得更加容易。

例如，考虑以下代码，它使用 `"arraybuffer"` 的 `responseType` 来将远程内容获取到一个存储原生二进制数据的 `ArrayBuffer` 对象中。

```js
var oReq = new XMLHttpRequest();

oReq.onload = function (e) {
  var arraybuffer = oReq.response; // 不是 responseText！
  /\* ... \*/
};
oReq.open("GET", url);
oReq.responseType = "arraybuffer";
oReq.send();
```

更多示例请参考 发送和接收二进制数据。

监测进度
----

`XMLHttpRequest` 提供了各种在请求被处理期间发生的事件以供监听。这包括定期进度通知、错误通知，等等。

支持 DOM 的 progress 事件监测之于 `XMLHttpRequest` 传输，遵循 Web API 进度事件规范：这些事件实现了 `ProgressEvent` 接口。

`progress`

检索的数据量发生了变化。

`load`

传输完成，所有数据保存在 `response` 中。

```js
var oReq = new XMLHttpRequest();

oReq.addEventListener("progress", updateProgress);
oReq.addEventListener("load", transferComplete);
oReq.addEventListener("error", transferFailed);
oReq.addEventListener("abort", transferCanceled);

oReq.open();

// ...

// 服务端到客户端的传输进程（下载）
function updateProgress(oEvent) {
  if (oEvent.lengthComputable) {
    var percentComplete = (oEvent.loaded / oEvent.total) \* 100;
    // ...
  } else {
    // 总大小未知时不能计算进程信息
  }
}

function transferComplete(evt) {
  console.log("The transfer is complete.");
}

function transferFailed(evt) {
  console.log("An error occurred while transferring the file.");
}

function transferCanceled(evt) {
  console.log("The transfer has been canceled by the user.");
}
```

第 3-6 行为多种事件添加了事件监听，这些事件在使用 `XMLHttpRequest` 执行数据传输时被发出。

**备注：** 你需要在请求调用 `open()` 之前添加事件监听。否则 `progress` 事件将不会被触发。

在上一个例子中，progress 事件被指定由 `updateProgress()` 函数处理，并接收到传输的总字节数和已经传输的字节数，它们分别在事件对象的 `total` 和 `loaded` 属性里。但是如果 `lengthComputable` 属性的值是 false，那么意味着总字节数是未知并且 total 的值为零。

progress 事件同时存在于下载和上传的传输。下载相关事件在 `XMLHttpRequest` 对象上被触发，就像上面的例子一样。上传相关事件在 `XMLHttpRequest.upload` 对象上被触发，像下面这样：

```js
var oReq = new XMLHttpRequest();

oReq.upload.addEventListener("progress", updateProgress);
oReq.upload.addEventListener("load", transferComplete);
oReq.upload.addEventListener("error", transferFailed);
oReq.upload.addEventListener("abort", transferCanceled);

oReq.open();
```

**备注：** progress 事件在使用 `file:` 协议的情况下是无效的。

**备注：** 从 Gecko 9.0 开始，进度事件现在可以依托于每一个传入的数据块，包括进度事件被触发前在已经接受了最后一个数据包且连接已经被关闭的情况下接收到的最后一个块。这种情况下，当该数据包的 load 事件发生时 progress 事件会被自动触发。这使你可以只关注 progress 事件就可以可靠的监测进度。

**备注：** 在 Gecko 12.0 中，当 `responseType` 为 "moz-blob" 时，如果你的 progress 事件被触发，则响应的值是一个包含了接收到的数据的 `Blob` 。

使用 `loadend` 事件可以侦测到所有的三种加载结束条件（`abort`、`load`，或 `error`）：

```js
req.addEventListener("loadend", loadEnd);

function loadEnd(e) {
  console.log(
    "The transfer finished (although we don't know if it succeeded or not).",
  );
}
```

需要注意的是，没有方法可以确切的知道 `loadend` 事件接收到的信息是来自何种条件引起的操作终止；但是你可以在所有传输结束的时候使用这个事件处理。

提交表单和上传文件
---------

`XMLHttpRequest` 的实例有两种方式提交表单：

* 使用 AJAX
* 使用 `FormData` API

 第二种方式（使用 `FormData` API）是最简单最快捷的，但是缺点是被收集的数据无法使用 JSON.stringify() 转换为一个 JSON 字符串。
 只使用 AJAX 则更为复杂，但也更灵活、更强大。

### 仅使用 XMLHttpRequest

在大多数用例中，提交表单时即便不使用 `FormData` API 也不会要求其他的 API。唯一的例外情况是，**如果你要上传一个或多个文件**，你需要额外的 `FileReader` API。

#### 提交方法简介

一个 html `<form>` 可以用以下四种方式发送：

* 使用 `POST` 方法，并将 `enctype` 属性设置为 `application/x-www-form-urlencoded` (默认)
* 使用 `POST` 方法，并将 `enctype` 属性设置为 `text/plain`
* 使用 `POST` 方法，并将 `enctype` 属性设置为 `multipart/form-data`
* 使用 `GET` 方法（这种情况下 `enctype` 属性会被忽略）

现在，我们提交一个表单，它里面有两个字段，分别被命名为 `foo` 和 `baz`。如果你用 `POST` 方法，那么服务器将会接收到一个字符串类似于下面三种情况之一，其中的区别依赖于你采用何种编码类型：

* 方法：`POST`；编码类型：`application/x-www-form-urlencoded`（默认）:
```

Content-Type: application/x-www-form-urlencoded

foo=bar&baz=The+first+line.%0D%0AThe+second+line.%0D%0A
```

* 方法：`POST`；编码类型：`text/plain`：
```

Content-Type: text/plain

foo=bar
baz=The first line.
The second line.
```

* 方法：`POST`；编码类型：`multipart/form-data`：
```

Content-Type: multipart/form-data; boundary=---------------------------314911788813839

-----------------------------314911788813839
Content-Disposition: form-data; name="foo"

bar
-----------------------------314911788813839
Content-Disposition: form-data; name="baz"

The first line.
The second line.

-----------------------------314911788813839--
```

相反的，如果你用 `GET` 方法，像下面这样的字符串将被简单的附加到 URL：
```

?foo=bar&baz=The%20first%20line.%0AThe%20second%20line.
```

#### 一个小框架

所有这些事情都是由浏览器在你提交一个 `<form>` 的时候自动完成的。但是如果你想要用 JavaScript 做同样的事情，你不得不告诉解释器所有的事。那么，如何发送表单这件事在使用纯粹的 AJAX 时会复杂到无法在这里解释清楚。基于这个原因，我们提供一个完整的（但仍然教条的）框架，它可以使用所有的四种提交方式，甚至上传文件：

```html
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Sending forms with pure AJAX – MDN</title>
    <script type="text/javascript">
 "use strict";

 /\*\
|\*|
|\*| :: XMLHttpRequest.prototype.sendAsBinary() Polyfill ::
|\*|
|\*| https://developer.mozilla.org/zh-CN/docs/DOM/XMLHttpRequest#sendAsBinary()
\\*/

 if (!XMLHttpRequest.prototype.sendAsBinary) {
 XMLHttpRequest.prototype.sendAsBinary = function (sData) {
 var nBytes = sData.length,
 ui8Data = new Uint8Array(nBytes);
 for (var nIdx = 0; nIdx < nBytes; nIdx++) {
 ui8Data[nIdx] = sData.charCodeAt(nIdx) & 0xff;
 }
 /\* send as ArrayBufferView...: \*/
 this.send(ui8Data);
 /\* ...or as ArrayBuffer (legacy)...: this.send(ui8Data.buffer); \*/
 };
 }

 /\*\
|\*|
|\*| :: AJAX Form Submit Framework ::
|\*|
|\*| https://developer.mozilla.org/zh-CN/docs/DOM/XMLHttpRequest\_API/Using\_XMLHttpRequest
|\*|
|\*| This framework is released under the GNU Public License, version 3 or later.
|\*| https://www.gnu.org/licenses/gpl-3.0-standalone.html
|\*|
|\*| Syntax:
|\*|
|\*| AJAXSubmit(HTMLFormElement);
\\*/

 var AJAXSubmit = (function () {
 function ajaxSuccess() {
 /\* console.log("AJAXSubmit - Success!"); \*/
 console.log(this.responseText);
 /\* you can get the serialized data through the "submittedData" custom property: \*/
 /\* console.log(JSON.stringify(this.submittedData)); \*/
 }

 function submitData(oData) {
 /\* the AJAX request... \*/
 var oAjaxReq = new XMLHttpRequest();
 oAjaxReq.submittedData = oData;
 oAjaxReq.onload = ajaxSuccess;
 if (oData.technique === 0) {
 /\* method is GET \*/
 oAjaxReq.open(
 "get",
 oData.receiver.replace(
 /(?:\?.\*)?$/,
 oData.segments.length > 0 ? "?" + oData.segments.join("&") : "",
 ),
 true,
 );
 oAjaxReq.send(null);
 } else {
 /\* method is POST \*/
 oAjaxReq.open("post", oData.receiver, true);
 if (oData.technique === 3) {
 /\* enctype is multipart/form-data \*/
 var sBoundary =
 "---------------------------" + Date.now().toString(16);
 oAjaxReq.setRequestHeader(
 "Content-Type",
 "multipart\/form-data; boundary=" + sBoundary,
 );
 oAjaxReq.sendAsBinary(
 "--" +
 sBoundary +
 "\r\n" +
 oData.segments.join("--" + sBoundary + "\r\n") +
 "--" +
 sBoundary +
 "--\r\n",
 );
 } else {
 /\* enctype is application/x-www-form-urlencoded or text/plain \*/
 oAjaxReq.setRequestHeader("Content-Type", oData.contentType);
 oAjaxReq.send(
 oData.segments.join(oData.technique === 2 ? "\r\n" : "&"),
 );
 }
 }
 }

 function processStatus(oData) {
 if (oData.status > 0) {
 return;
 }
 /\* the form is now totally serialized! do something before sending it to the server... \*/
 /\* doSomething(oData); \*/
 /\* console.log("AJAXSubmit - The form is now serialized. Submitting..."); \*/
 submitData(oData);
 }

 function pushSegment(oFREvt) {
 this.owner.segments[this.segmentIdx] += oFREvt.target.result + "\r\n";
 this.owner.status--;
 processStatus(this.owner);
 }

 function plainEscape(sText) {
 /\* How should I treat a text/plain form encoding?
 What characters are not allowed? this is what I suppose...: \*/
 /\* "4\3\7 - Einstein said E=mc2" ----> "4\\3\\7\ -\ Einstein\ said\ E\=mc2" \*/
 return sText.replace(/[\s\=\\]/g, "\\$&");
 }

 function SubmitRequest(oTarget) {
 var nFile,
 sFieldType,
 oField,
 oSegmReq,
 oFile,
 bIsPost = oTarget.method.toLowerCase() === "post";
 /\* console.log("AJAXSubmit - Serializing form..."); \*/
 this.contentType =
 bIsPost && oTarget.enctype
 ? oTarget.enctype
 : "application\/x-www-form-urlencoded";
 this.technique = bIsPost
 ? this.contentType === "multipart\/form-data"
 ? 3
 : this.contentType === "text\/plain"
 ? 2
 : 1
 : 0;
 this.receiver = oTarget.action;
 this.status = 0;
 this.segments = [];
 var fFilter = this.technique === 2 ? plainEscape : escape;
 for (var nItem = 0; nItem < oTarget.elements.length; nItem++) {
 oField = oTarget.elements[nItem];
 if (!oField.hasAttribute("name")) {
 continue;
 }
 sFieldType =
 oField.nodeName.toUpperCase() === "INPUT"
 ? oField.getAttribute("type").toUpperCase()
 : "TEXT";
 if (sFieldType === "FILE" && oField.files.length > 0) {
 if (this.technique === 3) {
 /\* enctype is multipart/form-data \*/
 for (nFile = 0; nFile < oField.files.length; nFile++) {
 oFile = oField.files[nFile];
 oSegmReq = new FileReader();
 /\* (custom properties:) \*/
 oSegmReq.segmentIdx = this.segments.length;
 oSegmReq.owner = this;
 /\* (end of custom properties) \*/
 oSegmReq.onload = pushSegment;
 this.segments.push(
 'Content-Disposition: form-data; name="' +
 oField.name +
 '"; filename="' +
 oFile.name +
 '"\r\nContent-Type: ' +
 oFile.type +
 "\r\n\r\n",
 );
 this.status++;
 oSegmReq.readAsBinaryString(oFile);
 }
 } else {
 /\* enctype is application/x-www-form-urlencoded or text/plain or
 method is GET: files will not be sent! \*/
 for (
 nFile = 0;
 nFile < oField.files.length;
 this.segments.push(
 fFilter(oField.name) +
 "=" +
 fFilter(oField.files[nFile++].name),
 )
 );
 }
 } else if (
 (sFieldType !== "RADIO" && sFieldType !== "CHECKBOX") ||
 oField.checked
 ) {
 /\* NOTE: this will submit \_all\_ submit buttons. Detecting the correct one is non-trivial. \*/
 /\* field type is not FILE or is FILE but is empty \*/
 this.segments.push(
 this.technique === 3 /\* enctype is multipart/form-data \*/
 ? 'Content-Disposition: form-data; name="' +
 oField.name +
 '"\r\n\r\n' +
 oField.value +
 "\r\n"
 : /\* enctype is application/x-www-form-urlencoded or text/plain or method is GET \*/
 fFilter(oField.name) + "=" + fFilter(oField.value),
 );
 }
 }
 processStatus(this);
 }

 return function (oFormElement) {
 if (!oFormElement.action) {
 return;
 }
 new SubmitRequest(oFormElement);
 };
 })();
 </script>
  </head>
  <body>
    <h1>Sending forms with pure AJAX</h1>

    <h2>Using the GET method</h2>

    <form
 action="register.php"
 method="get"
 onsubmit="AJAXSubmit(this); return false;">
      <fieldset>
        <legend>Registration example</legend>
        <p>
          First name: <input type="text" name="firstname" /><br />
          Last name: <input type="text" name="lastname" />
        </p>
        <p>
          <input type="submit" value="Submit" />
        </p>
      </fieldset>
    </form>

    <h2>Using the POST method</h2>
    <h3>Enctype: application/x-www-form-urlencoded (default)</h3>

    <form
 action="register.php"
 method="post"
 onsubmit="AJAXSubmit(this); return false;">
      <fieldset>
        <legend>Registration example</legend>
        <p>
          First name: <input type="text" name="firstname" /><br />
          Last name: <input type="text" name="lastname" />
        </p>
        <p>
          <input type="submit" value="Submit" />
        </p>
      </fieldset>
    </form>

    <h3>Enctype: text/plain</h3>

    <form
 action="register.php"
 method="post"
 enctype="text/plain"
 onsubmit="AJAXSubmit(this); return false;">
      <fieldset>
        <legend>Registration example</legend>
        <p>Your name: <input type="text" name="user" /></p>
        <p>
          Your message:<br />
          <textarea name="message" cols="40" rows="8"></textarea>
        </p>
        <p>
          <input type="submit" value="Submit" />
        </p>
      </fieldset>
    </form>

    <h3>Enctype: multipart/form-data</h3>

    <form
 action="register.php"
 method="post"
 enctype="multipart/form-data"
 onsubmit="AJAXSubmit(this); return false;">
      <fieldset>
        <legend>Upload example</legend>
        <p>
          First name: <input type="text" name="firstname" /><br />
          Last name: <input type="text" name="lastname" /><br />
          Sex:
          <input id="sex\_male" type="radio" name="sex" value="male" />
          <label for="sex\_male">Male</label>
          <input id="sex\_female" type="radio" name="sex" value="female" />
          <label for="sex\_female">Female</label><br />
          Password: <input type="password" name="secret" /><br />
          What do you prefer:
          <select name="image\_type">
            <option>Books</option>
            <option>Cinema</option>
            <option>TV</option>
          </select>
        </p>
        <p>
          Post your photos:
          <input type="file" multiple name="photos[]" />
        </p>
        <p>
          <input
 id="vehicle\_bike"
 type="checkbox"
 name="vehicle[]"
 value="Bike" />
          <label for="vehicle\_bike">I have a bike</label><br />
          <input
 id="vehicle\_car"
 type="checkbox"
 name="vehicle[]"
 value="Car" />
          <label for="vehicle\_car">I have a car</label>
        </p>
        <p>
          Describe yourself:<br />
          <textarea name="description" cols="50" rows="8"></textarea>
        </p>
        <p>
          <input type="submit" value="Submit" />
        </p>
      </fieldset>
    </form>
  </body>
</html>
```

要测试它的话，创建一个名为 `register.php` 的页面（作为示例表单的 `action` 属性）并且只输入以下内容：

```php
<?php
/\* register.php \*/

header("Content-type: text/plain");

/\*
NOTE: You should never use `print\_r()` in production scripts, or
otherwise output client-submitted data without sanitizing it first.
Failing to sanitize can lead to cross-site scripting vulnerabilities.
\*/

echo ":: data received via GET ::\n\n";
print\_r($\_GET);

echo "\n\n:: Data received via POST ::\n\n";
print\_r($\_POST);

echo "\n\n:: Data received as \"raw\" (text/plain encoding) ::\n\n";
if (isset($HTTP\_RAW\_POST\_DATA)) { echo $HTTP\_RAW\_POST\_DATA; }

echo "\n\n:: Files received ::\n\n";
print\_r($\_FILES);
```

激活这些代码的语法很简单：
```

AJAXSubmit(myForm);
```

**备注：** 该框架使用 `FileReader` API 进行文件的上传。这是一个较新的 API 并且还未在 IE9 及以下版本的浏览器中实现。因此，使用 AJAX 上传仍是一项**实验性的技术**。如果你不需要上传 二进制文件，该框架在大多数浏览器中运行良好。

**备注：** 发送二进制内容的最佳途径是通过 `ArrayBuffers` 或 `Blobs` 结合 `send()` 方法甚至 `FileReader` API 的 `readAsArrayBuffer()` 方法。但是，自从该脚本的目的变成处理 可字符串化 的原始数据以来，我们使用 `sendAsBinary()` 方法结合 `FileReader` API 的 `readAsBinaryString()` 方法。同样地，上述脚本仅当你处理小文件时行之有效。如果不打算上传二进制内容，就考虑使用 `FormData` API 来替代。

**备注：** 非标准的 `sendAsBinary` 方法从 Gecko 31 开始将会废弃并且会很快被移除。标准方法 `send(Blob data)` 将会取而代之。

### 使用 FormData 对象

`FormData` 构造函数能使你编译一个键/值对的集合，然后使用 `XMLHttpRequest` 发送出去。其主要用于发送表格数据，但是也能被单独用来传输表格中用户指定的数据。传输的数据格式与表格使用 `submit()` 方法发送数据的格式一致，如果该表格的编码类型被设为 "multipart/form-data"。FormData 对象可以被结合 `XMLHttpRequest` 的多种方法利用。例如，想了解如何利用 FormData 与 XMLHttpRequest，请转到使用 FormData 对象页面。为了说教的目的，这里有一个早期的示例，被转译成了使用 **`FormData` API** 的形式。注意以下代码片段：

```html
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" charset="UTF-8" />
    <title>Sending forms with FormData – MDN</title>
    <script>
 "use strict";

 function ajaxSuccess() {
 console.log(this.responseText);
 }

 function AJAXSubmit(oFormElement) {
 if (!oFormElement.action) {
 return;
 }
 var oReq = new XMLHttpRequest();
 oReq.onload = ajaxSuccess();
 if (oFormElement.method.toLowerCase() === "post") {
 oReq.open("post", oFormElement.action);
 oReq.send(new FormData(oFormElement));
 } else {
 var oField,
 sFieldType,
 nFile,
 sSearch = "";
 for (var nItem = 0; nItem < oFormElement.elements.length; nItem++) {
 oField = oFormElement.elements[nItem];
 if (!oField.hasAttribute("name")) {
 continue;
 }
 sFieldType =
 oField.nodeName.toUpperCase() === "INPUT"
 ? oField.getAttribute("type").toUpperCase()
 : "TEXT";
 if (sFieldType === "FILE") {
 for (
 nFile = 0;
 nFile < oField.files.length;
 sSearch +=
 "&" +
 escape(oField.name) +
 "=" +
 escape(oField.files[nFile++].name)
 );
 } else if (
 (sFieldType !== "RADIO" && sFieldType !== "CHECKBOX") ||
 oField.checked
 ) {
 sSearch += "&" + escape(oField.name) + "=" + escape(oField.value);
 }
 }
 oReq.open(
 "get",
 oFormElement.action.replace(
 /(?:\?.\*)?$/,
 sSearch.replace(/^&/, "?"),
 ),
 true,
 );
 oReq.send(null);
 }
 }
 </script>
  </head>
  <body>
    <h1>Sending forms with FormData</h1>

    <h2>Using the GET method</h2>

    <form
 action="register.php"
 method="get"
 onsubmit="AJAXSubmit(this); return false;">
      <fieldset>
        <legend>Registration example</legend>
        <p>
          First name: <input type="text" name="firstname" /><br />
          Last name: <input type="text" name="lastname" />
        </p>
        <p>
          <input type="submit" value="Submit" />
        </p>
      </fieldset>
    </form>

    <h2>Using the POST method</h2>
    <h3>Enctype: application/x-www-form-urlencoded (default)</h3>

    <form
 action="register.php"
 method="post"
 onsubmit="AJAXSubmit(this); return false;">
      <fieldset>
        <legend>Registration example</legend>
        <p>
          First name: <input type="text" name="firstname" /><br />
          Last name: <input type="text" name="lastname" />
        </p>
        <p>
          <input type="submit" value="Submit" />
        </p>
      </fieldset>
    </form>

    <h3>Enctype: text/plain</h3>

    <p>The text/plain encoding is not supported by the FormData API.</p>

    <h3>Enctype: multipart/form-data</h3>

    <form
 action="register.php"
 method="post"
 enctype="multipart/form-data"
 onsubmit="AJAXSubmit(this); return false;">
      <fieldset>
        <legend>Upload example</legend>
        <p>
          First name: <input type="text" name="firstname" /><br />
          Last name: <input type="text" name="lastname" /><br />
          Sex:
          <input id="sex\_male" type="radio" name="sex" value="male" />
          <label for="sex\_male">Male</label>
          <input id="sex\_female" type="radio" name="sex" value="female" />
          <label for="sex\_female">Female</label><br />
          Password: <input type="password" name="secret" /><br />
          What do you prefer:
          <select name="image\_type">
            <option>Books</option>
            <option>Cinema</option>
            <option>TV</option>
          </select>
        </p>
        <p>
          Post your photos:
          <input type="file" multiple name="photos[]" />
        </p>
        <p>
          <input
 id="vehicle\_bike"
 type="checkbox"
 name="vehicle[]"
 value="Bike" />
          <label for="vehicle\_bike">I have a bike</label><br />
          <input
 id="vehicle\_car"
 type="checkbox"
 name="vehicle[]"
 value="Car" />
          <label for="vehicle\_car">I have a car</label>
        </p>
        <p>
          Describe yourself:<br />
          <textarea name="description" cols="50" rows="8"></textarea>
        </p>
        <p>
          <input type="submit" value="Submit" />
        </p>
      </fieldset>
    </form>
  </body>
</html>
```

**备注：** 如之前所述，`FormData` 对象并不是 可字符串化 (stringifiable) 的对象。如果你想要字符串化一个提交数据，请使用这个 早期的纯 AJAX 例子. 同时也要注意，尽管这个例子中有一些 `file` `<input>` 字段，**但当你通过** `FormData` API 提交一个表格时，也无须使用 `FileReader` API: 文件被自动加载并上传。

获取最后修改日期
--------

```js
function getHeaderTime() {
  console.log(
    this.getResponseHeader("Last-Modified"),
  ); /\* 一个合法的 GMTString 日期或 null \*/
}

var oReq = new XMLHttpRequest();
oReq.open(
  "HEAD" /\* 仅需要头部信息 (headers) 时请使用 HEAD! \*/,
  "yourpage.html",
);
oReq.onload = getHeaderTime;
oReq.send();
```

### 最后修改日期改变后的操作

先创建两个函数：

```js
function getHeaderTime() {
  var nLastVisit = parseFloat(
    window.localStorage.getItem("lm\_" + this.filepath),
  );
  var nLastModif = Date.parse(this.getResponseHeader("Last-Modified"));

  if (isNaN(nLastVisit) || nLastModif > nLastVisit) {
    window.localStorage.setItem("lm\_" + this.filepath, Date.now());
    isFinite(nLastVisit) && this.callback(nLastModif, nLastVisit);
  }
}

function ifHasChanged(sURL, fCallback) {
  var oReq = new XMLHttpRequest();
  oReq.open("HEAD" /\* 使用 HEAD - 我们仅需要头部信息 (headers)! \*/, sURL);
  oReq.callback = fCallback;
  oReq.filepath = sURL;
  oReq.onload = getHeaderTime;
  oReq.send();
}
```

And to test:

```js
/\* 测试一下这个文件："yourpage.html"... \*/

ifHasChanged("yourpage.html", function (nModif, nVisit) {
  console.log(
    "The page '" +
      this.filepath +
      "' has been changed on " +
      new Date(nModif).toLocaleString() +
      "!",
  );
});
```

如果你想要了解当前页面是否发生了改变，请阅读这篇文章：`document.lastModified`。

跨站的 XMLHttpRequest
------------------

现代浏览器通过实现跨源资源共享（CORS）标准来支持跨站请求。只要服务器端的配置允许你从你的 Web 应用发送请求，就可以使用 `XMLHttpRequest`。否则，会抛出一个 `INVALID_ACCESS_ERR` 异常

绕过缓存
----

有一个跨浏览器兼容的方法，就是给 URL 添加时间戳。请确保你酌情地添加了 "?" or "&" 。例如，将：
```

http://example.com/bar.html -> http://example.com/bar.html?12345
http://example.com/bar.html?foobar=baz -> http://example.com/bar.html?foobar=baz&12345
```

因为本地缓存都是以 URL 作为索引的，这样就可以使每个请求都是唯一的，也就可以这样来绕开缓存。

你也可以用下面的方法自动更改缓存：

```js
const req = new XMLHttpRequest();

req.open("GET", url + (/\?/.test(url) ? "&" : "?") + new Date().getTime());
req.send(null);
```

安全性
---

要启用跨站脚本，推荐的做法是对 XMLHttpRequest 的响应使用 `Access-Control-Allow-Origin` 的 HTTP 标头。

### XMLHttpRequests 被停止

如果你的 XMLHttpRequest 收到 `status=0` 和 `statusText=null` 的返回，这意味着请求无法执行。就是未被发送的（`UNSENT`）。一个可能导致的原因是在 XMLHttpRequest 在执行 `open()` 时，`XMLHttpRequest` 的来源发生了改变。这种情况是可能发生的，例如，我们在一个窗口的 onunload 事件触发时在进行一个 XMLHttpRequest，之前创建的 XMLHttpRequest 仍然在那里，最后当这个窗口失去焦点、另一个窗口获得焦点时，它还是发送了请求（也就是 `open()`）。最有效的避免这个问题的方法是在关闭的窗口触发 `unload` 事件时为新窗口的 `DOMActivate` 事件设置一个监听器。

规范
--

| Specification |
| --- |
| XMLHttpRequest Standard # interface-xmlhttprequest |

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

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

参见
--

* XMLHttpRequest 中的 HTML
* HTTP 访问控制
* XMLHttpRequest - REST and the Rich User Experience
* "Using the XMLHttpRequest Object" (jibbering.com)
* The `XMLHttpRequest` object: WHATWG specification


同步和异步请求 - Web API 接口参考

同步和异步请求
=======

`XMLHttpRequest` 支持同步和异步通信。但是，一般来说，出于性能原因，异步请求应优先于同步请求。

同步请求阻止代码的执行，这会导致屏幕上出现“冻结”和无响应的用户体验。

异步请求
----

如果你使用`XMLHttpRequest`发送异步请求，那么当请求的响应数据完全收到之时，会执行一个指定的回调函数，而在执行异步请求的同时，浏览器会正常地执行其他事务的处理。

### 示例：在控制台输出页面源文件

这个例子演示了如何进行一个简单的异步请求。

```js
var xhr = new XMLHttpRequest();
xhr.open("GET", "/bar/foo.txt", true);
xhr.onload = function (e) {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      console.log(xhr.responseText);
    } else {
      console.error(xhr.statusText);
    }
  }
};
xhr.onerror = function (e) {
  console.error(xhr.statusText);
};
xhr.send(null);
```

第 2 行中指定第三个参数为 `true`，表示该请求应该以异步模式执行。

第 3 行创建一个事件处理函数对象，并将其分配给请求的 `onload` 属性。此处理程序查看请求的 `readyState`，以查看事务是否在第 4 行完成，如果是，并且 HTTP 状态为 200，则转储接收到的内容。如果发生错误，则显示错误消息。

第 15 行实际上启动了请求。只要请求的状态发生变化，就会调用回调程序。

### 示例：创建一个标准的方法来读取外部文件

在一些需求情况下，必须读取多个外部文件。这是一个标准的函数。该函数使用 `XMLHttpRequest` 对象进行异步请求。而且可以为每个文件读取完成后指定不同的回调函数。

```js
function xhrSuccess() {
  this.callback.apply(this, this.arguments);
}

function xhrError() {
  console.error(this.statusText);
}

function loadFile(url, callback /\*, opt\_arg1, opt\_arg2, ... \*/) {
  var xhr = new XMLHttpRequest();
  xhr.callback = callback;
  xhr.arguments = Array.prototype.slice.call(arguments, 2);
  xhr.onload = xhrSuccess;
  xhr.onerror = xhrError;
  xhr.open("GET", url, true);
  xhr.send(null);
}
```

用法：

```js
function showMessage(message) {
  console.log(message + this.responseText);
}

loadFile("message.txt", showMessage, "New message!\n\n");
```

实用函数 loadFile 的签名声明（i）要读取的目标 URL（通过 HTTP GET），（ii）成功完成 XHR 操作时执行的函数，以及（iii）任意列表的附加参数“通过“XHR 对象到成功回调函数。

第 1 行声明 XHR 操作成功完成时调用的函数。它又调用已经分配给 XHR 对象（第 7 行）属性的 loadFile 函数（本例中为函数 showMessage）的调用中指定的回调函数。提供给调用函数 loadFile 的附加参数（如果有的话）被“应用”到回调函数的运行中。

第 5 行声明 XHR 操作无法成功完成时调用的函数。

第 7 行存储 XHR 对象，成功回调函数作为 loadFile 的第二个参数给出。

第 12 行将参数赋给 loadFile 的调用。从第三个参数开始，收集所有剩余的参数，分配给变量 XHR 的 arguments 属性，传递给成功回调函数 xhrSuccess，最终提供给函数调用的回调函数（在本例中为 showMessage）xhrSuccess。

第 15 行为其第三个参数指定了 true，表示该请求应该被异步处理。

第 16 行实际启动请求。

### 示例：使用超时

你可以使用一个超时设置，来避免你的代码为了等候读取请求的返回数据长时间执行。超时毫秒数可以通过为 `XMLHttpRequest` 对象的 `timeout` 属性赋值来指定：

```js
function loadFile(url, timeout, callback) {
  var args = Array.prototype.slice.call(arguments, 3);
  var xhr = new XMLHttpRequest();
  xhr.ontimeout = function () {
    console.error("The request for " + url + " timed out.");
  };
  xhr.onload = function () {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        callback.apply(xhr, args);
      } else {
        console.error(xhr.statusText);
      }
    }
  };
  xhr.open("GET", url, true);
  xhr.timeout = timeout;
  xhr.send(null);
}
```

你还可以为 `timeout` 事件的 `ontimeout` 事件句柄指定事件处理函数。

用法：

```js
function showMessage(message) {
  console.log(message + this.responseText);
}

loadFile("message.txt", 2000, showMessage, "New message!\n");
```

如上，我们指定的超时时间为 2000 ms。

**备注：** `timeout` 属性添加于 Gecko 12.0。

同步请求
----

**备注：** 从 Gecko 30.0，Blink 39.0 和 Edge 13 开始，主线程上的同步请求由于对用户体验的负面影响而被弃用。

同步 XHR 通常会导致网络挂起。但开发人员通常不会注意到这个问题，因为在网络状况不佳或服务器响应速度慢的情况下，挂起只会显示同步 XHR 现在处于弃用状态。建议开发人员远离这个 API。

同步 XHR 不允许所有新的 XHR 功能（如 `timeout` 或 `abort`）。这样做会调用 `InvalidAccessError`。

### 示例：HTTP 同步请求

这个例子演示了如何进行一个简单的同步请求。

```js
var request = new XMLHttpRequest();
request.open("GET", "http://www.mozilla.org/", false);
request.send(null);
if (request.status === 200) {
  console.log(request.responseText);
}
```

第一行实例化一个 `XMLHttpRequest` 对象。第二行使用该对象打开一个 HTTP 请求，并指定使用 `HTTP GET` 方法来获取 Mozilla.org 主页内容，操作模式为同步。

第三行发送这个请求。参数为 `null` 表示 `GET` 请求不需要请求实体。

第四行为请求结束之后，检查请求状态码。如果状态码为 200，表示该请求成功，请求到的页面源文件会输出到控制台上。

### 示例：在 `Worker` 中使用 HTTP 同步请求

在 `Worker` 中使用 `XMLHttpRequest` 时，同步请求比异步请求更适合。

**`example.html`** (主页):

```html
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>MDN Example</title>
    <script type="text/javascript">
 var oMyWorker = new Worker("myTask.js");
 oMyWorker.onmessage = function (oEvent) {
 alert("Worker said: " + oEvent.data);
 };

 oMyWorker.postMessage("Hello");
 </script>
  </head>
  <body></body>
</html>
```

**`myFile.txt`** ( `XMLHttpRequest`对象同步请求的文件):
```

Hello World!!
```

**`myTask.js`** (包含了`Worker`代码):

```js
self.onmessage = function (oEvent) {
  if (oEvent.data === "Hello") {
    var oReq = new XMLHttpRequest();
    oReq.open("GET", "myFile.txt", false); // 同步请求
    oReq.send(null);
    self.postMessage(oReq.responseText);
  }
};
```

**备注：** 由于使用了`Worker`，所以该请求实际上也是异步的。

可以使用类似的方法，让脚本在后台与服务器交互，预加载某些内容。查看使用 Web workers了解更多详情。

### 将同步 XHR 用例调整到 Beacon API

在某些情况下，XMLHttpRequest 的同步使用是不可替代的，就像在 window.onunload和window.onbeforeunload 事件期间一样。你应该考虑使用带有 `Keepalive` 标志的 `fetch` API。当 `keepalive` 的 `fetch` 不可用时，可以考虑使用 navigator.sendBeacon API 可以支持这些用例，通常在提供良好 UX 的同时。

以下示例（来自 sendBeacon 文档）显示了一个理论分析代码，该代码尝试通过在卸载处理程序中使用同步 XMLHttpRequest 将数据提交给服务器。这导致页面的卸载被延迟。

```js
window.addEventListener("unload", logData, false);

function logData() {
  var client = new XMLHttpRequest();
  client.open("POST", "/log", false); // third parameter indicates sync xhr. :(
  client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
  client.send(analyticsData);
}
```

使用 **`sendBeacon()`** 方法，当用户代理有机会的时候，数据将被异步传输到 Web 服务器，**而不会延迟卸载或影响下一个导航的性能。**

以下示例显示了使用 **`sendBeacon()`** 方法将数据提交给服务器的理论分析代码模式。

```js
window.addEventListener("unload", logData, false);

function logData() {
  navigator.sendBeacon("/log", analyticsData);
}
```

参见
--

* `XMLHttpRequest`
* 使用 XMLHttpRequest
* `navigator.sendBeacon`


发送和接收二进制数据 - Web API 接口参考

发送和接收二进制数据
==========

使用 JavaScript 类型数组接受二进制数据
-------------------------

可以通过设置一个 XMLHttpRequest 对象的 `responseType`属性来改变一个从服务器上返回的响应的数据类型。可用的属性值为空字符串 (默认)，"arraybuffer"、"blob"、"document"、"json" 和 "text"。`response` 属性的值会根据 `responseType` 属性包含实体主体（entity body），它可能会是一个 `ArrayBuffer`、`Blob`、`Document`、`JSON`, string，或者为`NULL(如果请求未完成或失败)`

下例读取了一个二进制图像文件，并且由该文件的二进制原生字节创建了一个 8 位无符号整数的数组。注意，这不会解码图像，但会读取像素。你需要一个 png 解码库（png decoding library）。

```js
var oReq = new XMLHttpRequest();
oReq.open("GET", "/myfile.png", true);
oReq.responseType = "arraybuffer";

oReq.onload = function (oEvent) {
  var arrayBuffer = oReq.response; // 注意：不是 oReq.responseText
  if (arrayBuffer) {
    var byteArray = new Uint8Array(arrayBuffer);
    for (var i = 0; i < byteArray.byteLength; i++) {
      // 对数组中的每个字节进行操作
    }
  }
};

oReq.send(null);
```

也可以通过给 responseType 属性设置为 `“blob”`，将二进制文件读取为 `Blob` 类型的数据。

```js
var oReq = new XMLHttpRequest();
oReq.open("GET", "/myfile.png", true);
oReq.responseType = "blob";

oReq.onload = function (oEvent) {
  var blob = oReq.response;
  // ...
};

oReq.send();
```

在老的浏览器中接受二进制数据
--------------

下面的`load_binary_resource()` 方法可以从指定的 URL 那里加载二进制数据，并将数据返回给调用者。

```js
function load\_binary\_resource(url) {
  var req = new XMLHttpRequest();
  req.open("GET", url, false);
  //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
  req.overrideMimeType("text/plain; charset=x-user-defined");
  req.send(null);
  if (req.status != 200) return "";
  return req.responseText;
}
```

最为奇妙的操作在第五行，该行重写了默认的 MIME 类型，强制浏览器将该响应当成纯文本文件来对待，使用一个用户自定义的字符集。这样就是告诉了浏览器，不要去解析数据，直接返回未处理过的字节码。

```js
var filestream = load\_binary\_resource(url);
var abyte = filestream.charCodeAt(x) & 0xff; // 扔掉的高位字节 (f7)
```

上例从请求回来的二进制数据中得到偏移量为 x 处的字节。有效的偏移量范围是 0 到 `filestream.length-1`。

查看 使用 XMLHttpRequest 下载文件 了解详情，查看下载文件。

发送二进制数据
-------

XMLHttpRequest 对象的 `send` 方法已被增强，可以通过简单的传入一个 `ArrayBuffer`、`Blob` 或者 `File` 对象来发送二进制数据。

下例创建了一个文本文件，并使用 `POST` 方法将该文件发送到了服务器上。你也可以使用文本文件之外的其他二进制数据类型。

```js
var oReq = new XMLHttpRequest();
oReq.open("POST", url, true);
oReq.onload = function (oEvent) {
  // 上传完成后。
};

var bb = new BlobBuilder(); // 需要合适的前缀：window.MozBlobBuilder 或者 window.WebKitBlobBuilder
bb.append("abc123");

oReq.send(bb.getBlob("text/plain"));
```

将类型数组作为二进制数据发送
--------------

你可以将 JavaScript 类型数组作为二进制数据发送出去。

```js
var myArray = new ArrayBuffer(512);
var longInt8View = new Uint8Array(myArray);

for (var i = 0; i < longInt8View.length; i++) {
  longInt8View[i] = i % 255;
}

var xhr = new XMLHttpRequest();
xhr.open("POST", url, false);
xhr.send(myArray);
```

上例新建了一个 512 字节的 8 比特整数的数组并发送它，当然，你也可以发送任意的二进制数据。

**备注：** 从 Gecko 9.0 开始，添加了使用 XMLHttpRequest 发送 `ArrayBuffer` 对象的功能。

提交表单和上传文件
---------

请阅读此文

Firefox 私有方法
------------

下面的例子使用了 `POST` 请求，用 Firefox 私有的非标准方法 `sendAsBinary()` 将二进制数据以异步模式传输了出去。

```js
var req = new XMLHttpRequest();
req.open("POST", url, true);
// 这里应该设置适当的 MIME 请求头
req.setRequestHeader("Content-Length", 741);
req.sendAsBinary(aBody);
```

第四行将 Content-Length 请求头设置为 741，表示发送的数据长度为 741 个字节。你应该根据你要发送的数据的大小改变这个值。

第五行使用 `sendAsBinary()` 方法发送这个请求。

你也可以通过将一个 `nsIFileInputStream` 对象实例传给 `send()` 方法来发送二进制内容，这样的话，你不需要自己去设置 `Content-Length` 请求头的大小，程序会自动设置：

```js
// 新建一个文件流。
var stream = Components.classes[
  "@mozilla.org/network/file-input-stream;1"
].createInstance(Components.interfaces.nsIFileInputStream);
stream.init(file, 0x04 | 0x08, 0644, 0x04); // file 是一个 nsIFile 对象实例

// 设置文件的 MIME 类型
var mimeType = "text/plain";
try {
  var mimeService = Components.classes["@mozilla.org/mime;1"].getService(
    Components.interfaces.nsIMIMEService,
  );
  mimeType = mimeService.getTypeFromFile(file); // file 是一个 nsIFile 对象实例
} catch (oEvent) {
  /\* 丢弃异常，使用默认的 text/plain 类型 \*/
}

// 发送
var req = Components.classes[
  "@mozilla.org/xmlextras/xmlhttprequest;1"
].createInstance(Components.interfaces.nsIXMLHttpRequest);
req.open("PUT", url, false); // 同步模式！
req.setRequestHeader("Content-Type", mimeType);
req.send(stream);
```


AudioContext.listener - Web API 接口参考

AudioContext.listener
=====================

`AudioContext` 接口的 `listener` 属性返回一个 `AudioListener` 对象，可以用来实现 3D 音频空间化。

值
-

`AudioListener` 对象。

示例
--

**备注：** 如果需要完整的音频空间化例子，可以查看 panner-node 演示。

```js
const audioCtx = new AudioContext();
// 旧式的 webkit/blink 浏览器需要一个前缀

// …

const myListener = audioCtx.listener;
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-listener |

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

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

参见
--

* 使用 Web 音频 API (en-US)


AudioContext.currentTime - Web API 接口参考

AudioContext.currentTime
========================

`currentTime` 是 `AudioContext` 的一个只读属性，返回表示只增不减的硬件时间戳的双精度浮点数，可以用来控制音频回放，实现可视化时间轴等等。该值从 0 开始。

值
-

一个双精度浮点数。

示例
--

```js
const audioCtx = new AudioContext();
// 旧式的 webkit/blink 浏览器需要一个前缀

// …

console.log(audioCtx.currentTime);
```

降低时间精度
------

为了防止时间攻击和指纹识别，`audioCtx.currentTime` 的精度可能会根据浏览器的设置而被四舍五入。在 Firefox 中，`privacy.reduceTimerPrecision` 参数默认是启用的，在 Firefox 59 中默认为 20us；在 Firefox 60 中则为 2ms。

```js
// Firefox 60 中时间精度降低到了 2ms
audioCtx.currentTime;
// 23.404
// 24.192
// 25.514
// …

// 启用 `privacy.resistFingerprinting` 标志以降低时间精度
audioCtx.currentTime;
// 49.8
// 50.6
// 51.7
// …
```

在 Firefox 中，也可以启用 `privacy.resistFingerprinting` 标志，精度值将达到 100ms 或 `privacy.resistFingerprinting.reduceTimerPrecision.microseconds` 标志的值，会更大。

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-currenttime |

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

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

参见
--

* 使用 Web 音频 API (en-US)


AudioContext.createWaveShaper() - Web API 接口参考

AudioContext.createWaveShaper()
===============================

`AudioContext` 接口的 createWaveShaper() 方法创建了 表示非线性失真的`WaveShaperNode`。该节点通常被用来给音频添加失真效果

语法
--

```js
var audioCtx = new AudioContext();
var distortion = audioCtx.createWaveShaper();
```

### 返回

A `WaveShaperNode`.

示例
--

下面的例子展示了 AudioContext 创建一个波形整形器节点的基本用法。有关应用示例/信息，请查看我们的 voice-change-O-matic demo 演示（有关代码，请参阅app.js）。

**备注：** 实现失真曲线并不是简单的事情，你可能需要到处找资料来找到这样的算法。我们在Stack Overflow上找到了以下的失真曲线代码

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var distortion = audioCtx.createWaveShaper();

  ...

function makeDistortionCurve(amount) {
  var k = typeof amount === 'number' ? amount : 50,
    n_samples = 44100,
    curve = new Float32Array(n_samples),
    deg = Math.PI / 180,
    i = 0,
    x;
  for ( ; i < n_samples; ++i ) {
    x = i \* 2 / n_samples - 1;
    curve[i] = ( 3 + k ) \* x \* 20 \* deg / ( Math.PI + k \* Math.abs(x) );
  }
  return curve;
};

  ...

distortion.curve = makeDistortionCurve(400);
distortion.oversample = '4x';
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createwaveshaper |

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

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

参见
--

* 使用 Web Audio API (en-US)


AudioContext.sampleRate - Web API 接口参考

AudioContext.sampleRate
=======================

`AudioContext` 的 `sampleRate` 属性返回一个表示采样率（每秒采样数）的浮点数，同一个音频上下文中的所有节点采样率相同，所以不支持采样率转换。

值
-

一个浮点数。

示例
--

**备注：** 如果需要完整的 Web Audio 实现示例，可以查看 MDN Github repo 上的 Web 音频演示。不妨试试在浏览器控制台输入 `audioCtx.sampleRate`。

```js
const audioCtx = new AudioContext();
// 旧式的 webkit/blink 浏览器需要一个前缀

// …

console.log(audioCtx.sampleRate);
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-samplerate |

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

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

参见
--

* 使用 Web 音频 API (en-US)


BaseAudioContext：createAnalyser() 方法 - Web API 接口参考

BaseAudioContext：createAnalyser() 方法
====================================

`BaseAudioContext` (en-US) 接口的 `createAnalyser()` 方法创建一个`AnalyserNode`，它可以用来暴露音频时间和频率数据，以及创建数据可视化。

**备注：** `AnalyserNode()` 构造函数是创建 `AnalyserNode` 的推荐方法；请查看创建 AudioNode。

**备注：** 有关使用此节点的更多信息，请查看 `AnalyserNode` 页面。

语法
--

```js
createAnalyser()
```

### 参数

无。

### 返回值

一个 `AnalyserNode` 对象。

示例
--

下面的示例展示了 AudioContext 创建分析器节点的基本用法，然后用 requestAnimationFrame() 来反复获取时域数据，并绘制出当前音频输入的“示波器风格”输出。更多完整示例/信息请查看 Voice-change-O-matic 实例（参阅 app.js 的 108-193 行代码）。

```js
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const analyser = audioCtx.createAnalyser();

// …

analyser.fftSize = 2048;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);

// 绘制当前音频源的波形图

function draw() {
  drawVisual = requestAnimationFrame(draw);

  analyser.getByteTimeDomainData(dataArray);

  canvasCtx.fillStyle = "rgb(200, 200, 200)";
  canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);

  canvasCtx.lineWidth = 2;
  canvasCtx.strokeStyle = "rgb(0, 0, 0)";

  canvasCtx.beginPath();

  const sliceWidth = (WIDTH \* 1.0) / bufferLength;
  let x = 0;

  for (let i = 0; i < bufferLength; i++) {
    const v = dataArray[i] / 128.0;
    const y = (v \* HEIGHT) / 2;

    if (i === 0) {
      canvasCtx.moveTo(x, y);
    } else {
      canvasCtx.lineTo(x, y);
    }

    x += sliceWidth;
  }

  canvasCtx.lineTo(canvas.width, canvas.height / 2);
  canvasCtx.stroke();
}

draw();
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createanalyser |

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

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

参见
--

* 使用 Web Audio API


AudioContext.createBuffer() - Web API 接口参考

AudioContext.createBuffer()
===========================

音频环境`AudioContext` 接口的 `createBuffer() 方法用于新建一个空`白的 `AudioBuffer` 对象，以便用于填充数据，通过 `AudioBufferSourceNode` 播放。

更多关于音频片段 (Audio Buffer) 的细节，请参考`AudioBuffer`页面。

**备注：** `createBuffer()` 曾被用于接收压缩后的音频数据，并返回被解码的音频，但是这项功能现在已经被移除，因为所有的解码工作应当在主线程中被完成，`createBuffer()` 阻塞了其他代码的执行。异步方法 `decodeAudioData()` 能够完成相同的工作——传入一个压缩过的音频（如 MP3 格式的文件），并直接返回一个可以通过 `AudioBufferSourceNode` 播放的 `AudioBuffer`。因此播放诸如 MP3 等格式的压缩音频时，你应当使用 `decodeAudioData()` 方法。

语法
--

```js
AudioContext.createBuffer(Number numOfChannels, Number length, Number sampleRate);
```

### 参数

**备注：** 如果想深入了解 audio buffers 是如何工作的、这些参数的具体含义，请阅读这篇简短的指南： Audio buffers: frames, samples and channels（英）。

numOfChannels

 一个定义了 buffer 中包含的声频通道数量的整数。
 一个标准的实现必须包含至少 32 个声频通道。

length

一个代表 buffer 中的样本帧数的整数。

sampleRate

线性音频样本的采样率，即每一秒包含的关键帧的个数。实现过程中必须支持 22050 ～ 96000 的采样率。

### 返回值

一个 `AudioBuffer`。

示例
--

首先，我们将从几个浅显易懂的示例入手，来解释如何使用这些参数：

```js
var audioCtx = new AudioContext();
var buffer = audioCtx.createBuffer(2, 22050, 44100);
```

如果你这样调用，你将会得到一个立体声（两个声道）的音频片段 (Buffer)，当它在一个频率为 44100 赫兹（这是目前大部分声卡处理声音的频率）的音频环境 (`AudioContext`) 中播放的时候，会持续 0.5 秒：22050 帧 / 44100 赫兹 = 0.5 秒。

```js
var audioCtx = new AudioContext();
var buffer = audioCtx.createBuffer(1, 22050, 22050);
```

如果你这样调用，你将会得到一个单声道的音频片段 (Buffer)，当它在一个频率为 44100 赫兹的音频环境 (`AudioContext`) 中播放的时候，将会被自动按照 44100 赫兹*重采样*（因此也会转化为 44100 赫兹的片段），并持续 1 秒：44100 帧 / 44100 赫兹 = 1 秒。

**备注：** 音频重采样与图片的缩放非常类似：比如你有一个 16 x 16 的图像，但是你想把它填充到一个 32 x 32 大小的区域，你就要对它进行缩放（重采样）。得到的结果会是一个低品质的图像（图像会模糊或者有锯齿形的边缘，这取决于缩放采用的算法），但它能缩放原图像，并且缩放后的图像占用空间比相同大小的普通图像要小。重采样的音频原理相同——你能节省一些空间，但由此你也无法得到高频率的声音（高音区）。

现在让我们来看一个更加复杂的示例，我们将创建一个时长 2 秒的音频片段，并用白噪声填充它，之后通过一个 音频片段源节点 (`AudioBufferSourceNode`) 播放。代码中的注释应该能充分解释发生了什么。你可以 在线演示 ，或者 查看源代码 。

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var button = document.querySelector("button");
var pre = document.querySelector("pre");
var myScript = document.querySelector("script");

pre.innerHTML = myScript.innerHTML;

// 立体声
var channels = 2;
// 创建一个 采样率与音频环境 (AudioContext) 相同的 时长 2 秒的 音频片段。
var frameCount = audioCtx.sampleRate \* 2.0;

var myArrayBuffer = audioCtx.createBuffer(
  channels,
  frameCount,
  audioCtx.sampleRate,
);

button.onclick = function () {
  // 使用白噪声填充;
  // 就是 -1.0 到 1.0 之间的随机数
  for (var channel = 0; channel < channels; channel++) {
    // 这允许我们读取实际音频片段 (AudioBuffer) 中包含的数据
    var nowBuffering = myArrayBuffer.getChannelData(channel);
    for (var i = 0; i < frameCount; i++) {
      // Math.random() is in [0; 1.0]
      // audio needs to be in [-1.0; 1.0]
      nowBuffering[i] = Math.random() \* 2 - 1;
    }
  }

  // 获取一个 音频片段源节点 (AudioBufferSourceNode)。
  // 当我们想播放音频片段时，我们会用到这个源节点。
  var source = audioCtx.createBufferSource();
  // 把刚才生成的片段加入到 音频片段源节点 (AudioBufferSourceNode)。
  source.buffer = myArrayBuffer;
  // 把 音频片段源节点 (AudioBufferSourceNode) 连接到
  // 音频环境 (AudioContext) 的终节点，这样我们就能听到声音了。
  source.connect(audioCtx.destination);
  // 开始播放声源
  source.start();
};
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createbuffer |

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

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

参见
--

* 使用网络音频接口（英文） (en-US)


AudioContext.createScriptProcessor() - Web API 接口参考

AudioContext.createScriptProcessor()
====================================

**已弃用:** 不再推荐使用该特性。虽然一些浏览器仍然支持它，但也许已从相关的 web 标准中移除，也许正准备移除或出于兼容性而保留。请尽量不要使用该特性，并更新现有的代码；参见本页面底部的兼容性表格以指导你作出决定。请注意，该特性随时可能无法正常工作。

`AudioContext` 接口的`createScriptProcessor()` 方法创建一个`ScriptProcessorNode` 用于通过 JavaScript 直接处理音频。

语法
--

```js
var audioCtx = new AudioContext();
myScriptProcessor = audioCtx.createScriptProcessor(
  bufferSize,
  numberOfInputChannels,
  numberOfOutputChannels,
);
```

### 参数

`bufferSize`

 缓冲区大小，以样本帧为单位。具体来讲，缓冲区大小必须是下面这些值当中的某一个：256, 512, 1024, 2048, 4096, 8192, 16384. 如果不传，或者参数为 0，则取当前环境最合适的缓冲区大小，取值为 2 的幂次方的一个常数，在该 node 的整个生命周期中都不变。
 该取值控制着 `audioprocess` 事件被分派的频率，以及每一次调用多少样本帧被处理。较低 bufferSzie 将导致一定的延迟。较高的 bufferSzie 就要注意避免音频的崩溃和故障。推荐作者不要给定具体的缓冲区大小，让系统自己选一个好的值来平衡延迟和音频质量。

`numberOfInputChannels`

值为整数，用于指定输入 node 的声道的数量，默认值是 2，最高能取 32.

`numberOfOutputChannels`

值为整数，用于指定输出 node 的声道的数量，默认值是 2，最高能取 32.

**警告：** Webkit (version 31) 要求调用这个方法的时候必须传入一个有效的 bufferSize .

**备注：** `numberOfInputChannels`和`numberOfOutputChannels`的值不能同时为 0，二者同时为 0 是无效的

### 返回

A `ScriptProcessorNode`.

示例
--

下面的例子展示了一个 `ScriptProcessorNode` 的基本用法，数据源取自 `AudioContext.decodeAudioData`, 给每一个音频样本加一点白噪声，然后通过`AudioDestinationNode`播放 (其实这个就是系统的扬声器)。对于每一个声道和样本帧，在把结果当成输出样本之前，`scriptNode.onaudioprocess` 方法关联 `audioProcessingEvent` ，并用它来遍历每输入流的每一个声道，和每一个声道中的每一个样本，并添加一点白噪声。

**备注：** 完整的示例参照 script-processor-node github (查看源码 source code.)

```js
var myScript = document.querySelector("script");
var myPre = document.querySelector("pre");
var playButton = document.querySelector("button");

// Create AudioContext and buffer source
var audioCtx = new AudioContext();
source = audioCtx.createBufferSource();

// Create a ScriptProcessorNode with a bufferSize of 4096 and a single input and output channel
var scriptNode = audioCtx.createScriptProcessor(4096, 1, 1);
console.log(scriptNode.bufferSize);

// load in an audio track via XHR and decodeAudioData

function getData() {
  request = new XMLHttpRequest();
  request.open("GET", "viper.ogg", true);
  request.responseType = "arraybuffer";
  request.onload = function () {
    var audioData = request.response;

    audioCtx.decodeAudioData(
      audioData,
      function (buffer) {
        myBuffer = buffer;
        source.buffer = myBuffer;
      },
      function (e) {
        "Error with decoding audio data" + e.err;
      },
    );
  };
  request.send();
}

// Give the node a function to process audio events
scriptNode.onaudioprocess = function (audioProcessingEvent) {
  // The input buffer is the song we loaded earlier
  var inputBuffer = audioProcessingEvent.inputBuffer;

  // The output buffer contains the samples that will be modified and played
  var outputBuffer = audioProcessingEvent.outputBuffer;

  // Loop through the output channels (in this case there is only one)
  for (var channel = 0; channel < outputBuffer.numberOfChannels; channel++) {
    var inputData = inputBuffer.getChannelData(channel);
    var outputData = outputBuffer.getChannelData(channel);

    // Loop through the 4096 samples
    for (var sample = 0; sample < inputBuffer.length; sample++) {
      // make output equal to the same as the input
      outputData[sample] = inputData[sample];

      // add noise to each output sample
      outputData[sample] += (Math.random() \* 2 - 1) \* 0.2;
    }
  }
};

getData();

// wire up play button
playButton.onclick = function () {
  source.connect(scriptNode);
  scriptNode.connect(audioCtx.destination);
  source.start();
};

// When the buffer source stops playing, disconnect everything
source.onended = function () {
  source.disconnect(scriptNode);
  scriptNode.disconnect(audioCtx.destination);
};
```

规范
--

自 2014 年 8 月 29 日 Web Audio API 规范发布以来，此特性已被弃用。它不再有望成为标准。

它已被 AudioWorklet (en-US) 和 `AudioWorkletNode` 接口所取代。

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

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

See also
--------

* Using the Web Audio API (en-US)


AudioContext.createBufferSource() - Web API 接口参考

AudioContext.createBufferSource()
=================================

`createBufferSource()` 方法用于创建一个新的`AudioBufferSourceNode`接口，该接口可以通过`AudioBuffer` 对象来播放音频数据。`AudioBuffer`对象可以通过`AudioContext.createBuffer` 来创建或者通过 `AudioContext.decodeAudioData`成功解码音轨后获取。

语法
--

```js
var audioCtx = new AudioContext();
var source = audioCtx.createBufferSource();
```

返回
--

一个`AudioBufferSourceNode`对象。

例子
--

在这个例子中，我们将会创建一个 2 秒的缓冲器，并用白噪音填充它，然后通过`AudioBufferSourceNode`来播放它。

**备注：** You can also run the code live, or view the source.

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var button = document.querySelector("button");
var pre = document.querySelector("pre");
var myScript = document.querySelector("script");

pre.innerHTML = myScript.innerHTML;

// Stereo
var channels = 2;
// Create an empty two second stereo buffer at the
// sample rate of the AudioContext
var frameCount = audioCtx.sampleRate \* 2.0;

var myArrayBuffer = audioCtx.createBuffer(2, frameCount, audioCtx.sampleRate);

button.onclick = function () {
  // Fill the buffer with white noise;
  //just random values between -1.0 and 1.0
  for (var channel = 0; channel < channels; channel++) {
    // This gives us the actual ArrayBuffer that contains the data
    var nowBuffering = myArrayBuffer.getChannelData(channel);
    for (var i = 0; i < frameCount; i++) {
      // Math.random() is in [0; 1.0]
      // audio needs to be in [-1.0; 1.0]
      nowBuffering[i] = Math.random() \* 2 - 1;
    }
  }

  // Get an AudioBufferSourceNode.
  // This is the AudioNode to use when we want to play an AudioBuffer
  var source = audioCtx.createBufferSource();
  // set the buffer in the AudioBufferSourceNode
  source.buffer = myArrayBuffer;
  // connect the AudioBufferSourceNode to the
  // destination so we can hear the sound
  source.connect(audioCtx.destination);
  // start the source playing
  source.start();
};
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createbuffersource |

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

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

参见
--

* Using the Web Audio API (en-US)


BaseAudioContext.createOscillator() - Web API 接口参考

BaseAudioContext.createOscillator()
===================================

`BaseAudioContext` (en-US) 接口的 createOscillator() 方法创建一个 `OscillatorNode`，它是一个表示周期性波形的源。它基本上产生一个不变的音调。

Syntax
------
```

var oscillatorNode = audioCtx.createOscillator();
```

### Returns

An `OscillatorNode`.

Example
-------

以下示例显示了用于创建振荡器节点的 AudioContext 的基本用法。有关应用示例/信息，请查看我们的Violent Theremin demo（有关相关代码，请参阅see app.js）; 另请参阅我们的 OscillatorNode 页面以获取更多信息。

```js
// create web audio api context
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

// create Oscillator node
var oscillator = audioCtx.createOscillator();

oscillator.type = "square";
oscillator.frequency.setValueAtTime(3000, audioCtx.currentTime); // value in hertz
oscillator.connect(audioCtx.destination);
oscillator.start();
```

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

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createoscillator |

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

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

See also
--------

* Using the Web Audio API (en-US)


AudioContext.createBiquadFilter() - Web API 接口参考

AudioContext.createBiquadFilter()
=================================

`AudioContext` 的`createBiquadFilter()` 方法创建了一个 `BiquadFilterNode`, 它提供了一个可以指定多个不同的一般滤波器类型的双二阶滤波器。

语法
--

```js
var audioCtx = new AudioContext();
var biquadFilter = audioCtx.createBiquadFilter();
```

### 返回

一个 `BiquadFilterNode`.

示例
--

这个例子展示了一个利用 AudioContext 创建四项滤波器节点（Biquad filter node）的例子。想要查看完整工作的示例，请查看我们的 For voice-change-o-matic 样例（也可以查看 源码 ）.

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

//set up the different audio nodes we will use for the app
var analyser = audioCtx.createAnalyser();
var distortion = audioCtx.createWaveShaper();
var gainNode = audioCtx.createGain();
var biquadFilter = audioCtx.createBiquadFilter();
var convolver = audioCtx.createConvolver();

// connect the nodes together

source = audioCtx.createMediaStreamSource(stream);
source.connect(analyser);
analyser.connect(distortion);
distortion.connect(biquadFilter);
biquadFilter.connect(convolver);
convolver.connect(gainNode);
gainNode.connect(audioCtx.destination);

// Manipulate the Biquad filter

biquadFilter.type = "lowshelf";
biquadFilter.frequency.value = 1000;
biquadFilter.gain.value = 25;
```

规格
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createbiquadfilter |

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

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

相关
--

* Using the Web Audio API (en-US)


AudioContext.createDelay() - Web API 接口参考

AudioContext.createDelay()
==========================

`createDelay()` 是 `AudioContext` 的一个方法，作用是将输入音频信号延迟一定时间。（比如可以实现 对着话筒说句话，然后几秒后 这句话从音响里播放出来）

语法
--

```js
var audioCtx = new AudioContext();
var synthDelay = audioCtx.createDelay(maxDelayTime);
```

### 参数

*maxDelayTime*

设置最大允许延迟的时间，以“秒”为单位

### 返回

A `DelayNode` (en-US). The default `DelayNode.delayTime` (en-US) if no parameter is passed to `createDelay()` is 0 seconds.

以上是原文，大意是返回延时时间，没有设置时默认是 0

示例
--

首先是中文版的简洁的示例，这个例子中话筒里接收到的声音会延迟 3 秒从音响中播放。
```

window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;

try {//音频相关api
    var audioContext = new window.AudioContext();
    var synthDelay = audioContext.createDelay(5.0);
} catch (e) {
    alert("你浏览器不支持");
}

var error = function (error) {alert("有错误"); };

//以下是获取麦克风
if (navigator.getUserMedia) { //标准api
 navigator.getUserMedia({ "audio": true },
 function (stream) {
 micto(stream);    //具体工作
                   }, error);
}else if(navigator.webkitGetUserMedia) {   //webkit api
 navigator.webkitGetUserMedia({audio:true, video:  false },
 function (stream) {
  micto(stream); //具体工作
                   }, error);
 }else if (navigator.mozGetUserMedia) {  //火狐 api
 navigator.mozGetUserMedia({ "audio": true },
 function (stream) {
  micto(stream);//具体工作
                   }, error);
 }else if (navigator.msGetUserMedia) { //ie api
 navigator.msGetUserMedia({ "audio": true },
 function (stream) {
  micto(stream);//具体工作
                   }, error);
 } else {
   alert("你的浏览器版不支持这个api");
}

 var micto = function(stream) {

  synthDelay.delayTime.value = 3.0;   //延迟3秒

  var source = audioContext.createMediaStreamSource(stream);

  source.connect(synthDelay);

  synthDelay.connect(audioContext.destination);

      }
```

以下是英文版示例

We have created a simple example that allows you to play three different samples on a constant loop — see create-delay (you can also view the source code). If you just press the play buttons, the loops will start immediately; if you slide the sliders up to the right, then press the play buttons, a delay will be introduced, so the looping sounds don't start playing for a short amount of time.

```js
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();

var synthDelay = audioCtx.createDelay(5.0);

  ...

var synthSource;

playSynth.onclick = function() {
  synthSource = audioCtx.createBufferSource();
  synthSource.buffer = buffers[2];
  synthSource.loop = true;
  synthSource.start();
  synthSource.connect(synthDelay);
  synthDelay.connect(destination);
  this.setAttribute('disabled', 'disabled');
}

stopSynth.onclick = function() {
  synthSource.disconnect(synthDelay);
  synthDelay.disconnect(destination);
  synthSource.stop();
  playSynth.removeAttribute('disabled');
}

...

var delay1;
rangeSynth.oninput = function() {
delay1 = rangeSynth.value;
synthDelay.delayTime.value = delay1;
}
```

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

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createdelay |

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

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

See also
--------

* Using the Web Audio API (en-US)


BaseAudioContext.createConstantSource() - Web API 接口参考

BaseAudioContext.createConstantSource()
=======================================

**`createConstantSource()`** 是 `BaseAudioContext` (en-US) 接口的一个方法，用于生成一个 `ConstantSourceNode` (en-US) 对象，该对象是一个输出单信道（one-channel）音频信号的音频源，这些音频信号都拥有一个恒定的样本值。

Syntax
------
```

var constantSourceNode = AudioContext.createConstantSource()
```

### Parameters

None.

### Returns

A `ConstantSourceNode` (en-US) instance.

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

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createconstantsource |

Browser Compatibility
---------------------

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


BaseAudioContext.createPeriodicWave() - Web API 接口参考

BaseAudioContext.createPeriodicWave()
=====================================

`BaseAudioContext` (en-US) 接口的 `createPeriodicWave()` 方法用于创建可对 `OscillatorNode` 输出进行整形的 `PeriodicWave`（周期波）。

语法
--
```

var wave = AudioContext.createPeriodicWave(real, imag[, constraints]);
```

### 返回值

一个 `PeriodicWave`.

### 参数

`real`

一系列余弦术语 (传统上的 A 项)。

`imag`

一系列正弦项 (传统上的 B 项)。

`constraints` 可选

一个字典对象，用于指定是否禁用规范化（如果没有指定，则默认启动规范化）。它有一个属性：

* `disableNormalization`: 如果设置为 `true`，对周期波禁用规范化。默认值为 `false`.

**备注：** 如果使用规范化，生成波形的最大绝对峰值为 1。

例子
--

下面的例子为 `createPeriodicWave()` 的简单用法，创建包含简单正弦波的 `PeriodicWave` 对象。

```js
var real = new Float32Array(2);
var imag = new Float32Array(2);
var ac = new AudioContext();
var osc = ac.createOscillator();

real[0] = 0;
imag[0] = 0;
real[1] = 1;
imag[1] = 0;

var wave = ac.createPeriodicWave(real, imag, { disableNormalization: true });

osc.setPeriodicWave(wave);

osc.connect(ac.destination);

osc.start();
osc.stop(2);
```

这之所以有用是因为根据定义仅包含基本音调的声音是正弦波。

这里，我们创建一个具有两个值的 `PeriodicWave` (周期波) 。第一个值是 DC(直流) 偏移，它是振荡器启动的值。0 在这里是好的，因为我们想在 [-1.0; 1.0] 范围的中间开始曲线。

第二个值和后续值是正弦和余弦分量。你可以把它看做是傅里叶变换的结果，可以从时域值中获取频域值。这里，使用 `createPeriodicWave()`，你可以指定频率，浏览器会执行一个逆傅里叶变换，以获得振荡器的时域缓冲。这里，我们只在基础音上设置了一个全音量 (1.0) 的分量，所以我们得到一个正弦波。

傅里叶变换系数应该按升序给出（例如：

(

a
+
b
i

)

e
i

,

(

c
+
d
i

)

e

2
i

,

(

f
+
g
i

)

e

3
i

\left(a+bi\right)e^{i} , \left(c+di\right)e^{2i} , \left(f+gi\right)e^{3i}

等。）可以是正的或者是负的。一个简单的手动获取此类系数的方法是使用图形计算器，尽管这不是最好的。

规格
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createperiodicwave |

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

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

参见
--

* Using the Web Audio API (en-US)


AudioContext.onstatechange - Web API 接口参考

AudioContext.onstatechange
==========================

`AudioContext` 的 `onstatechange` 属性定义了一个事件处理器函数，触发 `statechange` (en-US) 会被调用，也就是说 audio context 的状态发生变化时会执行。

语法
--

```js
var audioCtx = new AudioContext();
audioCtx.onstatechange = function() { ... };
```

例子
--

下面这段代码是AudioContext states DEMO (直接运行) 中的，其中 `onstatechange` 处理器会在每次当前 `state` 发生变化时把它输出到控制台。

```js
audioCtx.onstatechange = function () {
  console.log(audioCtx.state);
};
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-onstatechange |

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

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

另见
--

* Using the Web Audio API (en-US)


AudioContext.createConvolver() - Web API 接口参考

AudioContext.createConvolver()
==============================

`AudioContext`的方法 `createConvolver()` 能创建一个 `ConvolverNode`，通常用来对你的音频应用混响效果。在 Convolution 规范定义 中查看更多信息。

语法
--

```js
var audioCtx = new AudioContext();
var convolver = audioCtx.createConvolver();
```

### 返回值

`ConvolverNode`对象。

例子
--

下面的例子展示了一个 AudioContext 创建一个 混响器节点 的基本使用方法。基本前提是你创建一个包含声音样本的 AudioBuffer 用作混响环境 (称之为*脉冲响应*,) 和在混响器中应用。下面的例子使用了一个简短的示例音乐厅人群效果，所以混响效果应用深度和回声。

更多完整例子请查看 Voice-change-O-matic demo (中 app.js 的代码)。

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var convolver = audioCtx.createConvolver();

  ...

// grab audio track via XHR for convolver node

var soundSource, concertHallBuffer;

ajaxRequest = new XMLHttpRequest();
ajaxRequest.open('GET', 'concert-crowd.ogg', true);
ajaxRequest.responseType = 'arraybuffer';

ajaxRequest.onload = function() {
  var audioData = ajaxRequest.response;
  audioCtx.decodeAudioData(audioData, function(buffer) {
      concertHallBuffer = buffer;
      soundSource = audioCtx.createBufferSource();
      soundSource.buffer = concertHallBuffer;
    }, function(e){"Error with decoding audio data" + e.err});
}

ajaxRequest.send();

  ...

convolver.buffer = concertHallBuffer;
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createconvolver |

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

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

另见
--

* Using the Web Audio API (en-US)


AudioContext.destination - Web API 接口参考

AudioContext.destination
========================

`AudioContext`的 `destination` 属性返回一个 `AudioDestinationNode`，表示 context 中所有音频的最终目标节点，一般是音频渲染设备，比如扬声器。

值
-

`AudioDestinationNode` 节点。

示例
--

**备注：** 如果需要完整的实现示例，可以去看看 MDN Github repo 的演示，比如 voice-change-o-matic。

```js
const audioCtx = new AudioContext();
// 旧式的 webkit/blink 浏览器需要一个前缀

const oscillatorNode = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();

oscillatorNode.connect(gainNode);
gainNode.connect(audioCtx.destination);
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-destination |

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

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

参见
--

* 使用 Web 音频 API (en-US)


AudioContext.createChannelMerger() - Web API 接口参考

AudioContext.createChannelMerger()
==================================

`AudioContext.createChannelMerger()` 方法，会创建一个 `ChannelMergerNode`，后者可以把多个音频流的通道整合到一个音频流。

语法
--

```js
var audioCtx = new AudioContext();
var merger = audioCtx.createChannelMerger(2);
```

### 参数

numberOfInputs

通道输入音频流的数量，输出流将包含这个数量的通道。默认值 6。

### 返回值

一个 `ChannelMergerNode`.

示例
--

下面的例子展示了如何分离立体音轨（就是一段音乐），处理使左右声道不同。使用的时候，需要指定 AudioNode.connect(AudioNode) 方法的第二个和第三个参数，分别用来指定通道链接来源的索引和输出的索引。

```js
var ac = new AudioContext();
ac.decodeAudioData(someStereoBuffer, function (data) {
  var source = ac.createBufferSource();
  source.buffer = data;
  var splitter = ac.createChannelSplitter(2);
  source.connect(splitter);
  var merger = ac.createChannelMerger(2);

  // Reduce the volume of the left channel only
  var gainNode = ac.createGain();
  gainNode.gain.value = 0.5;
  splitter.connect(gainNode, 0);

  // Connect the splitter back to the second input of the merger: we
  // effectively swap the channels, here, reversing the stereo image.
  gainNode.connect(merger, 0, 1);
  splitter.connect(merger, 1, 0);

  var dest = ac.createMediaStreamDestination();

  // Because we have used a ChannelMergerNode, we now have a stereo
  // MediaStream we can use to pipe the Web Audio graph to WebRTC,
  // MediaRecorder, etc.
  merger.connect(dest);
});
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createchannelmerger |

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

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

参见
--

* Using the Web Audio API (en-US)


AudioContext.decodeAudioData() - Web API 接口参考

AudioContext.decodeAudioData()
==============================

`AudioContext`接口的 `decodeAudioData()` 方法可用于异步解码音频文件中的 `ArrayBuffer`。`ArrayBuffer` 数据可以通过 `XMLHttpRequest` 和 `FileReader` 来获取。AudioBuffer 是通过 AudioContext 采样率进行解码的，然后通过回调返回结果。

这是从音频轨道创建用于 web audio API 音频源的首选方法。

语法
--

旧版的回调函数语法
```

audioCtx.decodeAudioData(audioData, function(decodedData) {
  // use the decoded data here
});
```

新版的 promise-based 语法：
```

audioCtx.decodeAudioData(audioData).then(function(decodedData) {
  // use the decoded data here
});
```

举例
--

在本章节中，我们将首先学习基于回调的系统，然后采用新的基于 promise-based 的语法

### 旧的回调语法

在这个事例中， `getData()` 方法使用 XHR 加载一个音轨，设置请求的 responsetype 为 ArrayBuffer 使它返回一个 arraybuffer 数据，然后存储在 audioData 变量中。然后我们将这个 arraybuffer 数据置于 `decodeAudioData()` 方法中使用，当成功解码 PCM Data 后通过回调返回，将返回的结果通过`AudioContext.createBufferSource()`接口进行处理并获得一个`AudioBufferSourceNode`, 将源连接至`AudioContext.destination`并将它设置为循环的。

通过按钮来运行 `getData()` 来获取音轨并播放它。当使用 `stop()` 方法后 source 将会被清除。

**备注：** You can run the example live (or view the source.)

```js
// define variables

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var source;

var pre = document.querySelector("pre");
var myScript = document.querySelector("script");
var play = document.querySelector(".play");
var stop = document.querySelector(".stop");

// use XHR to load an audio track, and
// decodeAudioData to decode it and stick it in a buffer.
// Then we put the buffer into the source

function getData() {
  source = audioCtx.createBufferSource();
  var request = new XMLHttpRequest();

  request.open("GET", "viper.ogg", true);

  request.responseType = "arraybuffer";

  request.onload = function () {
    var audioData = request.response;

    audioCtx.decodeAudioData(
      audioData,
      function (buffer) {
        source.buffer = buffer;

        source.connect(audioCtx.destination);
        source.loop = true;
      },

      function (e) {
        "Error with decoding audio data" + e.err;
      },
    );
  };

  request.send();
}

// wire up buttons to stop and play audio

play.onclick = function () {
  getData();
  source.start(0);
  play.setAttribute("disabled", "disabled");
};

stop.onclick = function () {
  source.stop(0);
  play.removeAttribute("disabled");
};

// dump script to pre element

pre.innerHTML = myScript.innerHTML;
```

### 新的 promise-based 语法

```js
ctx.decodeAudioData(compressedBuffer).then(function (decodedData) {
  // use the decoded data here
});
```

参数
--

ArrayBuffer

将会被解码的音频数据，可通过`XMLHttpRequest`或`FileReader`来获取。

DecodeSuccessCallback

当成功解码后会被调用的回调函数。该回调函数只有一个 AudioBuffer 类型参数。

DecodeErrorCallback

一个可选的错误回调函数。

返回
--

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

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-decodeaudiodata |

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

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

参见
--

* Using the Web Audio API (en-US)


AudioContext.state - Web API 接口参考

AudioContext.state
==================

`AudioContext`的 `state` 属性是只读的，返回 `AudioContext` 的当前状态。

语法
--

```js
var audioCtx = new AudioContext();
var myState = audioCtx.state;
```

### 返回值

`DOMString`，可能的值如下：

* `suspended`: audio context 被阻塞了 (用`AudioContext.suspend()` 方法)
* `running`: audio context 正常运行
* `closed`: audio context 被关闭了 (用`AudioContext.close()`方法)
例子
--

下面这段代码是AudioContext states demo (直接运行) 中的，其中`AudioContext.onstatechange`处理器会在每次当前状态发生变化时把它输出到控制台。

```js
audioCtx.onstatechange = function () {
  console.log(audioCtx.state);
};
```

规范
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-state |

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

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

另见
--

* Using the Web Audio API (en-US)


AudioContext.createChannelSplitter() - Web API 接口参考

AudioContext.createChannelSplitter()
====================================

The `createChannelSplitter()` method of the `AudioContext` Interface is used to create a `ChannelSplitterNode` (en-US), which is used to access the individual channels of an audio stream and process them separately.

Syntax
------

```js
var audioCtx = new AudioContext();
var splitter = audioCtx.createChannelSplitter(2);
```

### 参数

numberOfOutputs

你期待将输入音频分割成的声道道数目; 当不传入参数时，默认为 6

### Returns

一个 `ChannelSplitterNode` (en-US).

Example
-------

下面这个简单的例子告诉你怎样分割一个双声道音轨 (或者说一段音乐), 以及对于左右声道不同的处理。要使用它们，你需要用到`AudioNode.connect(AudioNode)` (en-US)方法的第二个和第三个参数，他们会指定链接声道源的序号和链接到的声道序号。

```js
var ac = new AudioContext();
ac.decodeAudioData(someStereoBuffer, function (data) {
  var source = ac.createBufferSource();
  source.buffer = data;
  var splitter = ac.createChannelSplitter(2);
  source.connect(splitter);
  var merger = ac.createChannelMerger(2);

  // Reduce the volume of the left channel only
  var gainNode = ac.createGain();
  gainNode.gain.value = 0.5;
  splitter.connect(gainNode, 0);

  // Connect the splitter back to the second input of the merger: we
  // effectively swap the channels, here, reversing the stereo image.
  gainNode.connect(merger, 0, 1);
  splitter.connect(merger, 1, 0);

  var dest = ac.createMediaStreamDestination();

  // Because we have used a ChannelMergerNode, we now have a stereo
  // MediaStream we can use to pipe the Web Audio graph to WebRTC,
  // MediaRecorder, etc.
  merger.connect(dest);
});
```

规格
--

| Specification |
| --- |
| Web Audio API # dom-baseaudiocontext-createchannelsplitter |

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

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

另见
--

* Using the Web Audio API (en-US)


ViewTransition：skipTransition() 方法 - Web API 接口参考

ViewTransition：skipTransition() 方法
==================================

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

`ViewTransition` 接口的 **`skipTransition()`** 方法跳过视图过渡的动画部分，但不跳过更新 DOM 的 `document.startViewTransition()` 回调函数。

语法
--

```js
skipTransition()
```

### 参数

无

### 返回值

`undefined`。

示例
--

```js
// 开始新的视图过渡
const transition = document.startViewTransition(() => displayNewImage());

// 跳过视图过渡动画，仅更新 DOM
transition.skipTransition();
```

规范
--

| Specification |
| --- |
| CSS View Transitions Module Level 1 # dom-viewtransition-skiptransition |

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

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

参见
--

* 使用 View Transitions API 实现平滑、简单的过渡


ViewTransition：finished 属性 - Web API 接口参考

ViewTransition：finished 属性
==========================

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

`ViewTransition` 接口的 **`finished`** 只读属性是一个 `Promise`。会在过渡动画完成（新的页面视图对用户可见且可交互）时兑现。

仅当传递给 `document.startViewTransition()` 的回调函数抛出异常或返回的 Promise 被拒绝时，`finished` 才会被拒绝，这表示页面的新状态未被创建。

如果过渡动画无法开始，或在动画期间使用 `ViewTransition.skipTransition()` 跳过了过渡动画，那么视图过渡依旧可以到达最终状态，因此 `finished` 依旧会被兑现。

值
-

一个 promise。

示例
--

### 不同的导航使用不同的过渡效果

有时，某些导航需要特定的过渡效果，例如后退导航可能与前进导航的过渡效果不同。处理这种情况的最佳实践是在 `<html>` 元素上设置一个类名以处理过渡（使用特定的选择器应用正确的动画），然后在过渡结束后删除该类名。

```js
async function handleTransition() {
  if (isBackNavigation) {
    document.documentElement.classList.add("back-transition");
  }

  const transition = document.startViewTransition(() =>
    updateTheDOMSomehow(data),
  );

  try {
    await transition.finished;
  } finally {
    document.documentElement.classList.remove("back-transition");
  }
}
```

**备注：** `isBackNavigation` 不是内置特性；它是一个理论上存在的函数，可以使用 Navigation API (en-US) 或类似的特性实现。

规范
--

| Specification |
| --- |
| CSS View Transitions Module Level 1 # dom-viewtransition-finished |

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

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

参见
--

* 使用 View Transitions API 实现平滑、简单的过渡


ViewTransition：ready 属性 - Web API 接口参考

ViewTransition：ready 属性
=======================

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

`ViewTransition` 接口的 **`ready`** 只读属性是一个 `Promise`。会在伪元素树被创建且过渡动画即将开始时兑现。

如果视图过渡无法开始，`ready` 就会被拒绝。这可能是由于错误的配置，例如重复的 `view-transition-name`，或者是因为 `Document.startViewTransition()` 的回调函数抛出异常或返回的 Promise 被拒绝。

值
-

一个 promise。

示例
--

在下面的示例中，`ready` 用于触发从用户点击位置开始的自定义圆形揭示视图过渡，动画由 Web Animations API 提供。

```js
// 保存最后一次点击事件
let lastClick;
addEventListener("click", (event) => (lastClick = event));

function spaNavigate(data) {
  // 为不支持此 API 的浏览器提供回退方案：
  if (!document.startViewTransition) {
    updateTheDOMSomehow(data);
    return;
  }

  // 获取点击位置，或者回退到屏幕中间
  const x = lastClick?.clientX ?? innerWidth / 2;
  const y = lastClick?.clientY ?? innerHeight / 2;
  // 获取到最远角的距离
  const endRadius = Math.hypot(
    Math.max(x, innerWidth - x),
    Math.max(y, innerHeight - y),
  );

  // 开始一次视图过渡：
  const transition = document.startViewTransition(() => {
    updateTheDOMSomehow(data);
  });

  // 等待伪元素创建完成：
  transition.ready.then(() => {
    // 新视图的根元素动画
    document.documentElement.animate(
      {
        clipPath: [
          `circle(0 at ${x}px ${y}px)`,
          `circle(${endRadius}px at ${x}px ${y}px)`,
        ],
      },
      {
        duration: 500,
        easing: "ease-in",
        // 指定要附加动画的伪元素
        pseudoElement: "::view-transition-new(root)",
      },
    );
  });
}
```

该动画还需要以下 CSS，以关闭默认的 CSS 动画并防止新旧视图状态以任何方式混合（新状态从旧状态上方“擦除”，而不是过渡）：

```css
::view-transition-image-pair(root) {
  isolation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
  display: block;
}
```

规范
--

| Specification |
| --- |
| CSS View Transitions Module Level 1 # dom-viewtransition-ready |

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

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

参见
--

* 使用 View Transitions API 实现平滑、简单的过渡


ViewTransition：updateCallbackDone 属性 - Web API 接口参考

ViewTransition：updateCallbackDone 属性
====================================

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

`ViewTransition` 接口的 **`updateCallbackDone`** 只读属性是一个 `Promise`。当传递给 `document.startViewTransition()` 的回调函数返回的 Promise 兑现时，该 Promise 也会兑现，当回调函数返回的 Promise 被拒绝时，该 Promise 也会被拒绝。

当你不关心过渡动画的成功或失败，而只关心 DOM 是否更新以及何时更新时，`updateCallbackDone` 非常有用。

值
-

一个 promise。

示例
--

```js
// 开始新的视图过渡
const transition = document.startViewTransition(() => displayNewImage());

transition.updateCallbackDone.then(() => {
  // 响应 DOM 更新成功
});
```

参见过渡作为增强功能以获取一个有用的示例。

规范
--

| Specification |
| --- |
| CSS View Transitions Module Level 1 # dom-viewtransition-updatecallbackdone |

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

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

参见
--

* 使用 View Transitions API 实现平滑、简单的过渡


WebGLRenderingContext.viewport() - Web API 接口参考

WebGLRenderingContext.viewport()
================================

WebGL API 的 **`WebGLRenderingContext.viewport()`** 方法，用来设置视口，即指定从标准设备到窗口坐标的 x、y 仿射变换。

语法
--
```

void gl.viewport(x, y, width, height);
```

### 参数

`x`

`GLint` (en-US)，用来设定视口的左下角水平坐标。默认值：0。

`y`

`GLint` (en-US)，用来设定视口的左下角垂直坐标。默认值：0。

width

非负数`Glsizei` (en-US)，用来设定视口的宽度。默认值：canvas 的宽度。

height

非负数`Glsizei` (en-US)，用来设定视口的高度。默认值：canvas 的高度。

### 返回值

None.

### 异常错误

只要宽度或高度有一个为负值，`gl.INVALID_VALUE` 错误将被抛出。

样例
--

当你第一次创建 WebGL 上下文的时候，视口的大小将和 `canvas` 的大小是匹配的。然而，如果你重新改变了 `canvas` 的大小，你需要告诉 WebGL 上下文设定新的视口。在这里，你可以使用 `gl.viewport`。

```js
gl.viewport(0, 0, canvas.width, canvas.height);
```

视口的宽度和高度的设定范围是依赖于底层如何实现的。如果你要获取这个范围，你可以查询`MAX_VIEWPORT_DIMS` 常量，它将返回 `Int32Array`。

```js
gl.getParameter(gl.MAX\_VIEWPORT\_DIMS);
// e.g. Int32Array[16384, 16384]
```

如果要获取当前的视口，则可以查询`VIEWPORT` 常量。

```js
gl.getParameter(gl.VIEWPORT);
// e.g. Int32Array[0, 0, 640, 480]
```

规范
--

| Specification |
| --- |
| WebGL Specification # 5.14.4 |

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

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

另见
--

* `WebGLRenderingContext.scissor()`
* `WebGLRenderingContext.getParameter()`


WebGLRenderingContext.getSupportedExtensions() - Web API 接口参考

WebGLRenderingContext.getSupportedExtensions()
==============================================

**`WebGLRenderingContext.getSupportedExtensions()`** 方法返回一个所有的支持 WebGL 扩展的列表。

语法
--

```js
getSupportedExtensions()
```

### 参数

无。

### 返回值

一个字符串数组，包含所有支持 WebGL 的扩展。

示例
--

```js
const canvas = document.getElementById("canvas");
gl = canvas.getContext("webgl");

const extensions = gl.getSupportedExtensions();
// Array [ 'ANGLE\_instanced\_arrays', 'EXT\_blend\_minmax', … ]
```

参见用于获得一个特定的扩展对象的 `WebGLRenderingContext.getExtension()` 方法。

WebGL 扩展
--------

所有 WebGL API 扩展都被注册在 WebGL Extension Registry 中。当前的扩展在这里列出。

规范
--

| Specification |
| --- |
| WebGL Specification # 5.14.14 |

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

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

参见
--

* `WebGLRenderingContext.getExtension()`
* webglreport.com


WebGLRenderingContext.linkProgram() - Web API 接口参考

WebGLRenderingContext.linkProgram()
===================================

`WebGLRenderingContext` 接口的\*\*linkProgram()\*\*方法链接给定的`WebGLProgram`，从而完成为程序的片元和顶点着色器准备 GPU 代码的过程。
语法
--
```

void gl.linkProgram(program);
```

### 参数

program

一个用于链接的 `WebGLProgram` 。

### 返回值

None.

例子
--

```js
var program = gl.createProgram();

// Attach pre-existing shaders
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);

gl.linkProgram(program);

if (!gl.getProgramParameter(program, gl.LINK\_STATUS)) {
  var info = gl.getProgramInfoLog(program);
  throw new Error("Could not compile WebGL program. \n\n" + info);
}
```

规范
--

| Specification |
| --- |
| WebGL Specification # 5.14.9 |

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

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

参考
--

* `WebGLRenderingContext.createProgram()`
* `WebGLRenderingContext.deleteProgram()`
* `WebGLRenderingContext.isProgram()`
* `WebGLRenderingContext.useProgram()`
* `WebGLRenderingContext.validateProgram()`
* `WebGLRenderingContext.getProgramParameter()` (en-US)
* `WebGLRenderingContext.getProgramInfoLog()`


WebGLRenderingContext.drawElements() - Web API 接口参考

WebGLRenderingContext.drawElements()
====================================

该 **`WebGLRenderingContext.drawElements()`** 方法 在 WebGL API 从数组数据渲染图元。

语法
--
```

void gl.drawElements(mode, count, type, offset);
```

### 参数

`mode`

`枚举类型` 指定要渲染的图元类型。可以是以下类型：

* `gl.POINTS`: 画单独的点。
* `gl.LINE_STRIP`: 画一条直线到下一个顶点。
* `gl.LINE_LOOP`: 绘制一条直线到下一个顶点，并将最后一个顶点返回到第一个顶点。
* `gl.LINES`: 在一对顶点之间画一条线。
* `gl.TRIANGLE_STRIP`
* `gl.TRIANGLE_FAN`
* `gl.TRIANGLES`: 为一组三个顶点绘制一个三角形。

count

`整数型` 指定要渲染的元素数量。

type

`枚举类型` 指定元素数组缓冲区中的值的类型。可能的值是：

* `gl.UNSIGNED_BYTE`
* `gl.UNSIGNED_SHORT`
* 当使用 `OES_element_index_uint` (en-US) 扩展时：
    + `gl.UNSIGNED_INT`

offset

`字节单位` 指定元素数组缓冲区中的偏移量。必须是给定类型大小的有效倍数。

### 返回值

None.

### 异常

* 如果 `mode` 不是正确值， `gl.INVALID_ENUM` 将会抛出错误异常。
* 如果`offset` 不是给定类型大小的有效倍数，`gl.INVALID_OPERATION` 将会抛出错误异常。
* 如果 `count` 是负的， `gl.INVALID_VALUE` 将会抛出错误异常。
例子
--

```js
gl.drawElements(gl.POINTS, 8, gl.UNSIGNED\_BYTE, 0);
```

规范
--

| Specification |
| --- |
| WebGL Specification # 5.14.11 |

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

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

相关参考
----

* `WebGLRenderingContext.drawArrays()`
* `OES_element_index_uint` (en-US)


WebGLRenderingContext.validateProgram() - Web API 接口参考

WebGLRenderingContext.validateProgram()
=======================================

**`WebGLRenderingContext.validateProgram()`** 是一种 WebGL API ，主要是用来验证 `WebGLProgram`。它在检查 WebGLProgram 程序是否链接成功的同时还会检查其是否能在当前的 WebGL 中使用。

语法
--
```

void gl.validateProgram(program);
```

### 参数

program

待验证的 `WebGLProgram`。

### 返回值

None.

Examples
--------

```js
var program = gl.createProgram();

// Attach pre-existing shaders
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);

gl.linkProgram(program);
gl.validateProgram(program);

if (!gl.getProgramParameter(program, gl.LINK\_STATUS)) {
  var info = gl.getProgramInfoLog(program);
  throw "不能编译 WebGL 程序。\n\n" + info;
}

gl.useProgram(program);
```

规范
--

| Specification |
| --- |
| WebGL Specification # 5.14.9 |

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

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

See also
--------

* `WebGLRenderingContext.createProgram()`
* `WebGLRenderingContext.deleteProgram()`
* `WebGLRenderingContext.isProgram()`
* `WebGLRenderingContext.linkProgram()`
* `WebGLRenderingContext.useProgram()`
* `WebGLRenderingContext.getProgramParameter()` (en-US)
* `WebGLRenderingContext.getProgramInfoLog()`


WebGLRenderingContext.createFramebuffer() - Web API 接口参考

WebGLRenderingContext.createFramebuffer()
=========================================

**`WebGLRenderingContext.creatFramebuffer()`** 是 WebGL API 的一个方法，用来创建和初始化`WebGLFramebuffer` 对象。语法
--
```

WebGLFramebuffer gl.createFramebuffer();
```

### 参数

None.

### 返回值

`WebGLFramebuffer` 对象

样例
--

### 创建一个帧缓冲区

```js
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
var framebuffer = gl.createFramebuffer();
```

说明
--

| Specification |
| --- |
| WebGL Specification # 5.14.6 |

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

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

另见
--

* `WebGLRenderingContext.bindFramebuffer()`
* `WebGLRenderingContext.deleteFramebuffer()`
* `WebGLRenderingContext.isFramebuffer()` (en-US)
* Other buffers: `WebGLBuffer`, `WebGLRenderbuffer`


WebGLRenderingContext.attachShader() - Web API 接口参考

WebGLRenderingContext.attachShader()
====================================

WebGL API 的 **`WebGLRenderingContext.attachShader()`** 方法负责往 `WebGLProgram` 添加一个片段或者顶点着色器。

语法
--
```

void gl.attachShader(program, shader);
```

### 参数

`program`

一个 `WebGLProgram` 对象

`shader`

一个类型为片段或者顶点的 `WebGLShader`

示例
--

以下代码为 `WebGLProgram` 添加一个预先定义好的着色器。

```js
var program = gl.createProgram();

// 添加一个预先定义的着色器
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);

gl.linkProgram(program);

if (!gl.getProgramParameter(program, gl.LINK\_STATUS)) {
  var info = gl.getProgramInfoLog(program);
  throw "Could not compile WebGL program. \n\n" + info;
}
```

规范
--

| Specification |
| --- |
| WebGL Specification # 5.14.9 |

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

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

相关资料
----

* `WebGLProgram`
* `WebGLShader`
* `WebGLRenderingContext.attachShader()`
* `WebGLRenderingContext.compileShader()`
* `WebGLRenderingContext.createProgram()`
* `WebGLRenderingContext.createShader()`
* `WebGLRenderingContext.deleteProgram()`
* `WebGLRenderingContext.deleteShader()`
* `WebGLRenderingContext.detachShader()`
* `WebGLRenderingContext.getAttachedShaders()` (en-US)
* `WebGLRenderingContext.getProgramParameter()` (en-US)
* `WebGLRenderingContext.getProgramInfoLog()`
* `WebGLRenderingContext.getShaderParameter()`
* `WebGLRenderingContext.getShaderPrecisionFormat()` (en-US)
* `WebGLRenderingContext.getShaderInfoLog()` (en-US)
* `WebGLRenderingContext.getShaderSource()`
* `WebGLRenderingContext.isProgram()`
* `WebGLRenderingContext.isShader()`
* `WebGLRenderingContext.linkProgram()`
* `WebGLRenderingContext.shaderSource()`
* `WebGLRenderingContext.useProgram()`
* `WebGLRenderingContext.validateProgram()`


WebGLRenderingContext.drawingBufferHeight - Web API 接口参考

WebGLRenderingContext.drawingBufferHeight
=========================================

**`WebGLRenderingContext.drawingBufferHeight`** 只读属性，指示当前绘图缓冲区的实际高度。它应当匹配与绘图上下文相关联的 `<canvas>` 元素的高度属性，如果实现未能提供所要求的高度，其值将有所不同。

语法
--
```

gl.drawingBufferHeight;
```

示例
--

指定 `<canvas>` 元素：

```html
<canvas id="canvas"></canvas>
```

你可以通过下面几行代码来获取绘图缓冲区的高度：

```js
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
gl.drawingBufferHeight; // 150
```

规范
--

| Specification |
| --- |
| WebGL Specification # DOM-WebGLRenderingContext-drawingBufferHeight |

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

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

相关链接
----

* `WebGLRenderingContext.drawingBufferWidth`
* `WebGLRenderingContext.viewport()`


WebGLRenderingContext.deleteShader() - Web API 接口参考

WebGLRenderingContext.deleteShader()
====================================

**`WebGLRenderingContext.deleteShader()`** 用于删除一个参数提供的 `WebGLShader`对象。如果该`WebGLShader`对象已经被删除，该方法不会有任何作用。Syntax
------
```

void gl.deleteShader(shader);
```

### 参数

shader

需要被删除的 `WebGLShader` 对象

### 返回值

None.

示例
--

### 删除一个着色器

```js
gl.deleteShader(shader);
```

规范
--

| Specification |
| --- |
| WebGL Specification # 5.14.9 |

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

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

其他相关
----

* `WebGLRenderingContext.createShader()`
* `WebGLRenderingContext.isShader()`
* `WebGLRenderingContext.getAttachedShaders()` (en-US)


WebGLRenderingContext.scissor() - Web API 接口参考

WebGLRenderingContext.scissor()
===============================

**`WebGLRenderingContext.scissor()`** 方法指定了一个裁剪区域，用来将绘图区域限制在其限定的盒形区域内。

语法
--
```

void gl.scissor(x, y, width, height);
```

### 参数

`x`

`GLint` (en-US) ，指定盒形裁剪区域左下角所在的横坐标，默认为 0。

`y`

`GLint` (en-US) 指定盒形裁剪区域左下角的纵坐标，默认为 0。

width

`Glsizei` (en-US)，用来确定盒型裁剪区域宽度的非负数，默认为 canvas 的宽度。

height

`Glsizei` (en-US) ，用以指定盒形裁剪区域高度的非负数，默认为 canvas 的高度。

### 返回值

无。

### 抛错

宽或高为负值时，抛出 `gl.INVALID_VALUE` 错误。

示例
--

当裁剪区域可用的时候，只有处于此区域内的像素才能由绘图命令修改。

```js
// turn on scissor test
gl.enable(gl.SCISSOR\_TEST);

// set the scissor rectangle
gl.scissor(x, y, width, height);

// execute drawing commands in the scissor box (e.g. clear)

// turn off scissor test again
gl.disable(gl.SCISSOR\_TEST);
```

通过查询 `SCISSOR_BOX` 常量来获取裁剪区域的维度信息，返回值是一个 `Int32Array` 对象。

```js
gl.scissor(0, 0, 200, 200);
gl.getParameter(gl.SCISSOR\_BOX);
// Int32Array[0, 0, 200, 200]
```

规范
--

| Specification |
| --- |
| WebGL Specification # 5.14.4 |

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

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

参见
--

* `WebGLRenderingContext.viewport()`
* `WebGLRenderingContext.enable()`
* `WebGLRenderingContext.disable()` (en-US)


WebGLRenderingContext.bindAttribLocation() - Web API 接口参考

WebGLRenderingContext.bindAttribLocation()
==========================================

WebGL API 的 **`WebGLRenderingContext.bindAttribLocation()`** 方法将通用顶点索引绑定到属性变量。

语法
--
```

void gl.bindAttribLocation(program, index, name);
```

### 参数

`program`

要绑定的`WebGLProgram` 对象。

`index`

`GLuint` (en-US) 指定要绑定的通用顶点的索引。

`name`

`DOMString`指定要绑定到通用顶点索引的变量的名称。该名称不能以“webgl\_”或“\_webgl\_”开头，因为这些名称将保留供 WebGL 使用。

### 返回值

无。

示例
--
```

gl.bindAttribLocation(program, colorLocation, 'vColor');
```

规范
--

| Specification |
| --- |
| WebGL Specification # 5.14.9 |

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

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

另见
--

* `WebGLRenderingContext.getActiveAttrib()` (en-US)
* `WebGLRenderingContext.getAttribLocation()`
* `WebGLRenderingContext.getVertexAttrib()` (en-US)