MouseEvent.pageX - Web API 接口参考

MouseEvent.pageX
================

**`pageX`** 是一个由 `MouseEvent` 接口返回的相对于整个文档的 x（水平）坐标以像素为单位的只读属性。

这个属性将基于文档的边缘，考虑任何页面的水平方向上的滚动。举个例子，如果页面向右滚动 200px 并出现了滚动条，这部分在窗口之外，然后鼠标点击距离窗口左边 100px 的位置，pageX 所返回的值将是 300。

起初这个属性被定义为长整型。CSSOM 视图模块将它重新定位为双浮点数类型。请参阅浏览器兼容性部分了解详情。

语法
--
```

var pos = event.pageX
```

例子
--
```

var pageX = event.pageX;
```

规范
--

| Specification |
| --- |
| CSSOM View Module # dom-mouseevent-pagex |

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

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

参考
--

* `UIEvent.pageX`


MouseEvent.screenX - Web API 接口参考

MouseEvent.screenX
==================

**`screenX`** 是 `MouseEvent` 的只读属性，提供鼠标在全局（屏幕）中的水平坐标（偏移量）。

**备注：** 在多屏显示的环境下，水平对齐的屏幕将被视为单个设备，因此 `screenX` 值的范围将增加到屏幕的组合宽度。

值
-

一个双精度浮点数（`double`）。

早期版本的规范将该值定义为整数值的像素数。

示例
--

这个示例展示了当触发 `mousemove` 事件时鼠标的坐标。

### HTML

```html
<p>移动鼠标以查看其坐标。</p>
<p id="screen-log"></p>
```

### JavaScript

```js
let screenLog = document.querySelector("#screen-log");
document.addEventListener("mousemove", logKey);
function logKey(e) {
  screenLog.innerText = `
 屏幕坐标 X/Y: ${e.screenX}, ${e.screenY}
 客户端坐标 X/Y: ${e.clientX}, ${e.clientY}`;
}
```

### 结果

### 路由事件

当你在窗口、文档或其他较大的元素上捕获事件时，可以获取事件的坐标（例如点击事件）然后作出正确的处理，如下所示：

```js
function checkClickMap(e) {
  if (e.screenX < 50) doRedButton();
  if (50 <= e.screenX && e.screenX < 100) doYellowButton();
  if (e.screenX >= 100) doRedButton();
}
```

规范
--

| Specification |
| --- |
| UI Events # dom-mouseevent-screenx |

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

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

参见
--

* `MouseEvent`
* `screenY`
* `clientX` / `clientY`


MouseEvent.x - Web API 接口参考

MouseEvent.x
============

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

**`MouseEvent.x`** 是 `MouseEvent.clientX` 属性的别名。

规范
--

| Specification |
| --- |
| CSSOM View Module # dom-mouseevent-x |

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

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


MouseEvent.ctrlKey - Web API 接口参考

MouseEvent.ctrlKey
==================

**`MouseEvent.ctrlKey`** 是只读属性，可返回一个布尔值，当 ctrl 键被按下，返回 true，否则返回 false。

语法
--
```

var ctrlKeyPressed = instanceOfMouseEvent.ctrlKey
```

### 返回值

A boolean

示例
--

```js
<html>
<head>
<title>ctrlKey example</title>

<script type="text/javascript">

function showChar(e){
  alert(
    "Key Pressed: " + String.fromCharCode(e.charCode) + "\n"
    + "charCode: " + e.charCode + "\n"
    + "CTRL key pressed: " + e.ctrlKey + "\n"
  );
}

</script>
</head>

<body onkeypress="showChar(event);">
<p>Press any character key, with or without holding down the CTRL key.<br />
You can also use the SHIFT key together with the CTRL key.</p>
</body>
</html>
```

规范
--

| Specification |
| --- |
| UI Events # dom-mouseevent-ctrlkey |

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

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

相关链接
----

* `MouseEvent`


MouseEvent.clientX - Web API 接口参考

MouseEvent.clientX
==================

**`MouseEvent.clientX`** 是只读属性，它提供事件发生时的应用客户端区域的水平坐标 (与页面坐标不同)。例如，不论页面是否有水平滚动，当你点击客户端区域的左上角时，鼠标事件的 `clientX` 值都将为 0。最初这个属性被定义为长整型（long integer），如今 **CSSOM** 视图模块将其重新定义为双精度浮点数（double float）。你可以查阅浏览器兼容性部分的文档来进一步了解有关信息。

语法
--
```

var x = instanceOfMouseEvent.clientX
```

### `返回值`

被 CSSOM View Module 重新定义为一个 `double` 类型的浮点值。原来这个属性是被定义为一个 `long` 整数。可以在 "浏览器兼容性" 那里查看详细内容。

示例
--

```html
<!doctype html>
<html>
  <head>
    <title>clientX/clientY example</title>

    <script>
 function showCoords(evt) {
 alert(
 "clientX value: " +
 evt.clientX +
 "\n" +
 "clientY value: " +
 evt.clientY +
 "\n",
 );
 }
 </script>
  </head>
  <body onmousedown="showCoords(event)">
    <p>To display the mouse coordinates click anywhere on the page.</p>
  </body>
</html>
```

规范
--

| Specification |
| --- |
| UI Events # dom-mouseevent-clientx |

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

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

相关
--

* `MouseEvent`
* `clientY`
* `screenX` / `screenY`


MouseEvent.buttons - Web API 接口参考

MouseEvent.buttons
==================

只读属性 **`MouseEvent.buttons`** 指示事件触发时哪些鼠标按键被按下。

每一个按键都用一个给定的数（见下文）表示。如果同时多个按键被按下，buttons 的值为各键对应值做与计算（+）后的值。例如，如果右键（2）和滚轮键（4）被同时按下，buttons 的值为 2 + 4 = 6。

**备注：** 属性 MouseEvent.button 和 MouseEvent.buttons 是不同的。MouseEvent.buttons 可指示任意鼠标事件中鼠标的按键情况，而 MouseEvent.button 只能保证在由按下和释放一个或多个按键时触发的事件中获得正确的值。

语法
--

```js
var buttonPressed = instanceOfMouseEvent.buttons;
```

返回值
---

一个数字，用来标识鼠标按下的一个或者多个按键。如果按下的键为多个，则值等于所有按键对应数值进行或 (|) 运算的结果。

* `0`: 没有按键或者是没有初始化
* `1`: 鼠标左键
* `2`: 鼠标右键
* `4`: 鼠标滚轮或者是中键
* `8`: 第四按键 (通常是“浏览器后退”按键)
* `16` : 第五按键 (通常是“浏览器前进”)
Specifications
--------------

| Specification |
| --- |
| UI Events # dom-mouseevent-buttons |

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

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

See also
--------

* `MouseEvent`


MouseEvent.y - Web API 接口参考

MouseEvent.y
============

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

**`MouseEvent.y`** 属性是 `MouseEvent.clientY` 属性的别称。

规范
--

| Specification |
| --- |
| CSSOM View Module # dom-mouseevent-y |

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

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


MouseEvent.clientY - Web API 接口参考

MouseEvent.clientY
==================

**`MouseEvent.clientY`** 是只读属性，它提供事件发生时的应用客户端区域的垂直坐标 (与页面坐标不同)。例如，当你点击客户端区域的左上角时，鼠标事件的 `clientY` 值为 0，这一值与页面是否有垂直滚动无关。

语法
--
```

var y = instanceOfMouseEvent.clientY
```

### Return value

被 CSSOM View Module 重新定义为一个 `double` 类型的浮点值。原来这个属性是被定义为一个 `long` 整数。可以在 "浏览器兼容性" 那里查看详细内容。

### Example

```js
<html>
<head>
<title>clientX\clientY example</title>

<script type="text/javascript">
function showCoords(evt){
  alert(
    "clientX value: " + evt.clientX + "\n"
    + "clientY value: " + evt.clientY + "\n"
  );
}
</script>
</head>

<body onmousedown="showCoords(event)">
<p>To display the mouse coordinates click anywhere on the page.</p>
</body>
</html>
```

规范
--

| Specification |
| --- |
| UI Events # dom-mouseevent-clienty |

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

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

查看更多
----

* `MouseEvent`
* `clientX`
* `screenX` / `screenY`


MouseEvent.button - Web API 接口参考

MouseEvent.button
=================

\*\*`MouseEvent.button`\*\*是只读属性，它返回一个值，代表用户按下并触发了事件的鼠标按键。

这个属性只能够表明在触发事件的单个或多个按键按下或释放过程中哪些按键被按下了。因此，它对判断`mouseenter`, `mouseleave`, `mouseover`, `mouseout` `mousemove`这些事件并不可靠。

用户可能会改变鼠标按键的配置，因此当一个事件的 **`MouseEvent.button`** 值为 0 时，它可能不是由物理上设备最左边的按键触发的。但是对于一个标准按键布局的鼠标来说就会是左键。

**备注：** `MouseEvent.buttons` 属性可指示任意鼠标事件中鼠标的按键情况，因此不要把它和 MouseEvent.button 属性弄混淆了。

语法
--

```js
var buttonPressed = instanceOfMouseEvent.button;
```

### 返回值

一个数值，代表按下的鼠标按键：

* `0`：主按键，通常指鼠标左键或默认值（译者注：如 document.getElementById('a').click() 这样触发就会是默认值）
* `1`：辅助按键，通常指鼠标滚轮中键
* `2`：次按键，通常指鼠标右键
* `3`：第四个按钮，通常指浏览器后退按钮
* `4`：第五个按钮，通常指浏览器的前进按钮

对于配置为左手使用的鼠标，按键操作将正好相反。此种情况下，从右至左读取值。

示例
--

### HTML

```html
<button id="button" oncontextmenu="event.preventDefault();">
  Click here with your mouse...
</button>
<p id="log"></p>
```

### JavaScript

```js
let button = document.querySelector("#button");
let log = document.querySelector("#log");
button.addEventListener("mouseup", logMouseButton);

function logMouseButton(e) {
  if (typeof e === "object") {
    switch (e.button) {
      case 0:
        log.textContent = "Left button clicked.";
        break;
      case 1:
        log.textContent = "Middle button clicked.";
        break;
      case 2:
        log.textContent = "Right button clicked.";
        break;
      default:
        log.textContent = `Unknown button code: ${e.button}`;
    }
  }
}
```

### 结果

规范
--

| Specification |
| --- |
| UI Events # dom-mouseevent-button |

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

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

参阅
--

* `"MouseEvent"`


MouseEvent.pageY - Web API 接口参考

MouseEvent.pageY
================

**`pageY`** 是一个只读属性，它返回触发事件的位置相对于整个 document 的 Y 坐标值。由于其参考物是整个 dom，所以这个值受页面垂直方向的滚动影响。例如：当你垂直方向向下滚动了 50 pixel，那么你在顶端进行点击的时候，获取的 **`pageY`** 为 50pixed 而不是 0。最初这个属性被定义为长整型（long integer），如今 **CSSOM** 视图模块将其重新定义为双精度浮点数（double float）。你可以查阅浏览器兼容性部分的文档来进一步了解有关信息。

语法
--
```

var pos = event.pageY
```

示例
--
```

var pageY = event.pageY;
```

规范
--

| Specification |
| --- |
| CSSOM View Module # dom-mouseevent-pagey |

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

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

更多请见
----

* `UIEvent.pageY`


MouseEvent() - Web API 接口参考

MouseEvent()
============

**`MouseEvent()`** 构造器创建一个 `MouseEvent`。

语法
--
```

 event = new MouseEvent(typeArg, mouseEventInit);
```

### 形参

*typeArg*

`DOMString` 格式的事件名称。

*mouseEventInit* 可选

初始化 `MouseEvent` 的字典，有下列属性字段：

* `"screenX"`，`long` 型可选，默认为 `0`，设置鼠标事件发生时相对于用户屏幕的水平坐标位置；该操作并不会改变真实鼠标的位置。
* `"screenY"`，`long` 型可选，默认为 `0`，设置鼠标事件发生时相对于用户屏幕的垂直坐标位置；该操作并不会改变真实鼠标的位置。
* `"clientX"`，`long` 型可选，默认为 `0`，设置鼠标事件时相对于客户端窗口的水平坐标位置；该操作并不会改变真实鼠标的位置。
* `"clientY"`，`long` 型可选，默认为 `0`，设置鼠标事件时相对于客户端窗口的垂直坐标位置；该操作并不会改变真实鼠标的位置。
* `"ctrlKey"`，`Boolean` 型可选，默认为`false`，标明是否同时按下
 `ctrl`
 键。
* `"shiftKey"`，`Boolean` 型可选，默认为`false`，标明是否同时按下
 `shift`
 键。
* `"altKey"`，`Boolean` 型可选，默认为 `false`，标明是否同时按下
 `alt`
 键。
* `"metaKey"`，`Boolean` 型可选，默认为`false`，标明是否同时按下
 `meta`
 键。
* `"button"`，`short` 型可选，默认为 `0`，描述了当事件发生时，哪个按键被按下或释放：

| 值 | 含义 |
| --- | --- |
| `0` | 主按键被按下（通常为左键）或未初始化 |
| `1` | 辅助按键被按下 (通常为中键) |
| `2` | 次按键被按下 (通常为右键) |
* `"buttons"`，无符号 `short` 型可选，默认为 `0`，描述了当事件发生时哪些按键被按下：

| 位域值（Bit-field value） | 含义 |
| --- | --- |
| `0` | 无按键被按下 |
| `1` | 主按键被按下 (通常为左键) |
| `2` | 次按键被按下 (通常为右键) |
| `4` | 辅助按键被按下 (通常为中键) |
* `"relatedTarget"`，`EventTarget` 型可选，默认为 `null`，若事件为 `mouseenter` 或 `mouseover`，则表示刚离开的元素；若事件为 `mouseout` 或 `mouseleave`，则表示刚进入的元素。
* `"region"`，`DOMString` 型可选，默认为`null`，标明点击事件影响的区域 DOM 的 id。不影响任何区域的话，请传`null`值。

在一些实现中，为屏幕和客户端字段传递数值以外的任何东西都会抛出 `TypeError`。

规范
--

| Specification |
| --- |
| UI Events # dom-mouseevent-mouseevent |

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

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

参见
--

* `MouseEvent`，对象结构接口。


MouseEvent.screenY - Web API 接口参考

MouseEvent.screenY
==================

**`screenY`** 是 `MouseEvent` 的只读属性，提供鼠标在全局（屏幕）中的垂直坐标（偏移量）。

值
-

一个双精度浮点数（`double`）。

早期版本的规范定义将其一个整数值的像素数。

示例
--

这个示例展示了当触发 `mousemove` 事件时鼠标的坐标。

### HTML

```html
<p>移动鼠标以查看其坐标。</p>
<p id="screen-log"></p>
```

### JavaScript

```js
let screenLog = document.querySelector("#screen-log");
document.addEventListener("mousemove", logKey);
function logKey(e) {
  screenLog.innerText = `
 屏幕坐标 X/Y: ${e.screenX}, ${e.screenY}
 客户端坐标 X/Y: ${e.clientX}, ${e.clientY}`;
}
```

### 结果

规范
--

| Specification |
| --- |
| UI Events # dom-mouseevent-screeny |

### 浏览器兼容性

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

参见
--

* `MouseEvent`
* `screenX`
* `clientX` / `clientY`


MouseEvent.offsetY - Web API 接口参考

MouseEvent.offsetY
==================

MouseEvent 接口的只读属性 **offsetY** 规定了事件对象与目标节点的内填充边（padding edge）在 Y 轴方向上的偏移量。

语法
--
```

var yOffset = instanceOfMouseEvent.offsetY;
```

### 返回值

一个`双精度` 浮点值。早期的规范将其规定为整数值。详见浏览器兼容性部分。

规范
--

| Specification |
| --- |
| CSSOM View Module # dom-mouseevent-offsety |

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

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

相关内容
----

* `MouseEvent`


MouseEvent.movementX - Web API 接口参考

MouseEvent.movementX
====================

**`MouseEvent.movementX`** 是只读属性，它提供了当前事件和上一个`mousemove`事件之间鼠标在水平方向上的移动值。换句话说，这个值是这样计算的 : `currentEvent.movementX = currentEvent.screenX - previousEvent.screenX`.

语法
--
```

var xShift = instanceOfMouseEvent.movementX
```

### 返回值

一个数字

规范
--

| Specification |
| --- |
| Pointer Lock 2.0 # dom-mouseevent-movementx |

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

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

相关
--

* `MouseEvent`
* Pointer Lock (en-US)


MouseEvent.shiftKey - Web API 接口参考

MouseEvent.shiftKey
===================

**`MouseEvent.shiftKey`** 是只读属性，指出触发鼠标事件时是否按住了 `shift` 键

Syntax
------
```

var shiftKeyPressed = instanceOfMouseEvent.shiftKey
```

### Return value

A boolean

Example
-------

```js
<html>
<head>
<title>shiftKey example</title>

<script type="text/javascript">

function showChar(e){
  alert(
    "Key Pressed: " + String.fromCharCode(e.charCode) + "\n"
    + "charCode: " + e.charCode + "\n"
    + "SHIFT key pressed: " + e.shiftKey + "\n"
    + "ALT key pressed: " + e.altKey + "\n"
  );
}

</script>
</head>

<body onkeypress="showChar(event);">
<p>Press any character key, with or without holding down
 the SHIFT key.<br />
You can also use the SHIFT key together with the ALT key.</p>
</body>
</html>
```

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

| Specification |
| --- |
| UI Events # dom-mouseevent-shiftkey |

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

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

See also
--------

* `MouseEvent`


MouseEvent.relatedTarget - Web API 接口参考

MouseEvent.relatedTarget
========================

只读属性 **`MouseEvent.relatedTarget`** 是鼠标事件的次要目标（如果存在），它包括：

| 事件名称 | `target` | `relatedTarget` |
| --- | --- | --- |
| `focusin` (en-US) | `EventTarget` 获取焦点 | `EventTarget` 失去焦点 |
| `focusout` | `EventTarget` 失去焦点 | The `EventTarget` 获取焦点 |
| `mouseenter` | 指针设备进入`EventTarget` | 指针设备离开`EventTarget` |
| `mouseleave` | 指针设备离开 `EventTarget` | 指针设备进入 `EventTarget` |
| `mouseout` | 指针设备离开 `EventTarget` | The `EventTarget` |
| `mouseover` | 指针设备进入 `EventTarget` | 指针设备离开 `EventTarget` |
| `dragenter` | 指针设备进入 `EventTarget` | 指针设备离开 `EventTarget` |

如果事件没有次要目标，`relatedTarget` 将返回 `null`.

语法
--
```

var target = instanceOfMouseEvent.relatedTarget
```

### 返回值

`EventTarget` 对象或者 `null`.

示例
--

尝试将你的鼠标移入移出红色和蓝色方块。

### HTML

```html
<body id="body">
  <div id="outer">
    <div id="red"></div>
    <div id="blue"></div>
  </div>
  <p id="log"></p>
</body>
```

### CSS

```css
#outer {
  width: 250px;
  height: 125px;
  display: flex;
}

#red {
  flex-grow: 1;
  background: red;
}

#blue {
  flex-grow: 1;
  background: blue;
}

#log {
  max-height: 120px;
  overflow-y: scroll;
}
```

### JavaScript

```js
const mouseoutLog = document.getElementById("log"),
  red = document.getElementById("red"),
  blue = document.getElementById("blue");

red.addEventListener("mouseover", overListener);
red.addEventListener("mouseout", outListener);
blue.addEventListener("mouseover", overListener);
blue.addEventListener("mouseout", outListener);

function outListener(event) {
  let related = event.relatedTarget ? event.relatedTarget.id : "unknown";

  mouseoutLog.innerText = `\nfrom ${event.target.id} into ${related} ${mouseoutLog.innerText}`;
}

function overListener(event) {
  let related = event.relatedTarget ? event.relatedTarget.id : "unknown";

  log.innerText = `\ninto ${event.target.id} from ${related} ${mouseoutLog.innerText}`;
}
```

### 结果

规范
--

| Specification |
| --- |
| UI Events # dom-mouseevent-relatedtarget |

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

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

参见
--

* `MouseEvent`
* Comparison of Event Targets (en-US)


MouseEvent.metaKey - Web API 接口参考

MouseEvent.metaKey
==================

**`MouseEvent.metaKey`** 为只读属性，返回一个`布尔值`，在鼠标事件发生时，用于指示 `Meta` 键是按下状态（`true`），还是释放状态（`false`）。

**备注：** 在 MAC 键盘上，表示 Command 键（`⌘`），在 Windows 键盘上，表示 Windows 键（`⊞`）。

语法
--
```

var metaKeyPressed = instanceOfMouseEvent.metaKey
```

### 返回值

一个布尔值。

示例
--

```js
function goInput(e) {
  // 检测 metaKey 值
  if (e.metaKey) {
    // 继续处理事件
    superSizeOutput(e);
  } else {
    doOutput(e);
  }
}
```

规范
--

| Specification |
| --- |
| UI Events # dom-mouseevent-metakey |

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

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

相关链接
----

* `MouseEvent`


MouseEvent.initMouseEvent() - Web API 接口参考

MouseEvent.initMouseEvent()
===========================

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

**`MouseEvent.initMouseEvent()`** 方法用以在鼠标事件创建时 (一般用 `Document.createEvent()`方法创建) 初始化其属性的值。

事件初始化是在事件被`Document.createEvent()`方法创建后必需的。这个方法必须在事件被`EventTarget.dispatchEvent()`方法发送出来前调用。一旦事件被发送后，它将不再起任何作用。

**备注：** **不要再用此方法，已过时。**

使用特定的事件构造器来替代它，像 `MouseEvent()`。创建并发送事件 (en-US) 页面里有更多的使用信息。

语法
--
```

event.initMouseEvent(type, canBubble, cancelable, view,
                     detail, screenX, screenY, clientX, clientY,
                     ctrlKey, altKey, shiftKey, metaKey,
                     button, relatedTarget);
```

### 形参

*`type`*

设置事件类型`type` 的字符串，包含以下几种鼠标事件：`click`，`mousedown`，`mouseup`，`mouseover`，`mousemove`，`mouseout`。

*`canBubble`*

是否可以冒泡。取值集合见`Event.bubbles`。

*`cancelable`*

是否可以阻止事件默认行为。取值集合见`Event.cancelable`。

*`view`*

事件的 AbstractView 对象引用，这里其实指向`window` 对象。取值集合见 `UIEvent.view`。

*`detail`*

事件的鼠标点击数量。取值集合见`Event.detail` (en-US)。

*`screenX`*

事件的屏幕的 x 坐标。取值集合见`MouseEvent.screenX`。

*`screenY`*

事件的屏幕的 y 坐标。取值集合见`MouseEvent.screenY`。

*`clientX`*

事件的客户端 x 坐标。取值集合见`MouseEvent.clientX`。

*`clientY`*

事件的客户端 y 坐标。取值集合见`MouseEvent.clientY`。

*`ctrlKey`*

事件发生时

`control`

键是否被按下。取值集合见`MouseEvent.ctrlKey`。

*`altKey`*

事件发生时

`alt`

键是否被按下。取值集合见`MouseEvent.altKey`。

*`shiftKey`*

事件发生时

`shift`

键是否被按下。取值集合见`MouseEvent.shiftKey`。

*`metaKey`*

事件发生时

`meta`

键是否被按下。取值集合见`MouseEvent.metaKey`。

*`button`*

鼠标按键值 `button`。

*`relatedTarget`*

事件的相关对象。只在某些事件类型有用 (例如 `mouseover` ?和 `mouseout`)。其他的传 null。

示例
--

### HTML

```html
<div style="background:red;width:180px;padding:10px;">
  <div id="out"></div>
  <input type="text" />
</div>
```

### JavaScript

```js
document.body.onclick = function () {
  e = arguments[0];
  var dt = e.target,
    stag = dt.tagName.toLowerCase();
  document.getElementById("out").innerHTML = stag;
};
var simulateClick = function () {
  var evt = document.createEvent("MouseEvents");
  evt.initMouseEvent(
    "click",
    true,
    true,
    window,
    0,
    0,
    0,
    80,
    20,
    false,
    false,
    false,
    false,
    0,
    null,
  );
  document.body.dispatchEvent(evt);
};
simulateClick(); //Why it can not show "input" ?
```

这里有个在线演示

规范
--

此特性不属于任何规范，也不再有望成为标准。

请使用 `MouseEvent()` 构造函数代替。

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

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

参阅
--

* `MouseEvent()`构造器，更标准的创建`MouseEvent`对象方法。
* `Event.initEvent()`可以简单达到相同目的的方法。它已过时不再使用。


MouseEvent.mozInputSource - Web API 接口参考

MouseEvent.mozInputSource
=========================

**非标准:** 该特性是非标准的，请尽量不要在生产环境中使用它！

`MouseEvent`中的 **`MouseEvent.mozInputSource`** 是只读属性，它提供触发事件的设备信息。例如，当一个鼠标事件发生时，你能根据 **`MouseEvent.mozInputSource`** 属性判断该事件是由鼠标还是由触屏设备触发的（这将影响到你对于事件发生坐标解释的精确度）。

语法
--
```

var source = event.mozInputSource;
```

### 值

下列值都是合法的

| 常量名称 | 值 | 描述 |
| --- | --- | --- |
| `MOZ_SOURCE_UNKNOWN` | 0 | 该事件是由未知设备触发的。 |
| `MOZ_SOURCE_MOUSE` | 1 | 该事件是由鼠标（或类似的设备）触发的。 |
| `MOZ_SOURCE_PEN` | 2 | 该事件是由触屏笔在写字板上触发的。 |
| `MOZ_SOURCE_ERASER` | 3 | 该事件是由触屏橡皮擦在写字板上触发的。 |
| `MOZ_SOURCE_CURSOR` | 4 | 该事件是由指针触发的。 |
| `MOZ_SOURCE_TOUCH` | 5 | 该事件是在触屏设备上触发的。 |
| `MOZ_SOURCE_KEYBOARD` | 6 | 该事件是由键盘触发的。 |

详述
--

无


MouseEvent.movementY - Web API 接口参考

MouseEvent.movementY
====================

`MouseEvent` 接口的 **`MouseEvent.movementY`** 只读属性提供了当前事件和上一个 `mousemove` 事件之间鼠标在竖直方向上的移动值。换句话说，这个值是这样计算的：`currentEvent.movementY = currentEvent.screenY - previousEvent.screenY`。

语法
--
```

var yShift = instanceOfMouseEvent.movementY
```

### 返回值

A number

规范
--

| Specification |
| --- |
| Pointer Lock 2.0 # dom-mouseevent-movementy |

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

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

相关
--

* `MouseEvent`
* Pointer Lock (en-US)


MouseEvent.offsetX - Web API 接口参考

MouseEvent.offsetX
==================

`MouseEvent` 接口的只读属性 **offsetX** 规定了事件对象与目标节点的内填充边（padding edge）在 X 轴方向上的偏移量。

语法
--
```

var xOffset = instanceOfMouseEvent.offsetX;
```

### 返回值

一个`双精度` 浮点值。早期的规范将其规定为整数值。详见浏览器兼容性部分。

规范
--

| Specification |
| --- |
| CSSOM View Module # dom-mouseevent-offsetx |

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

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

相关内容
----

* `MouseEvent`


使用可读流 - Web API 接口参考

使用可读流
=====

作为一个 JavaScript 开发者，以编程的方式逐块地读取和操作从网络上获取的数据是非常实用的！但是你要如何使用 Stream API 的可读流功能呢？可以在这篇文章看到基本介绍。

**备注：** 本文要求你已理解可读流相关知识，并了解相关的高级概念，如果还不了解，建议你先查看流的概念和简介以及掌握 Stream API 概念然后再阅读此文。

**备注：** 如果你正在查询关于可写流的信息，你可以尝试阅读使用可写流 (en-US)。

寻找一些示例
------

我们将在这篇文章中看到各种示例，它们取自我们的 dom-examples/streams 仓库。你可以在那里发现各种源代码，以及关联的示例。

使用流的方式处理 Fetch
--------------

Fetch API 允许你跨网络获取资源，它提供了现代化的 API 去替代 XHR。它有一系列的优点，真正好的是，浏览器最近增加了将 fetch 响应作为可读流使用的能力。

`Request.body` 和 `Response.body` 属性也是这样，它们将主体内容暴露作为一个可读流的 getter。

正如我们的简单流式读取示例所展示的（也可以参见在线演示），暴露它仅是需要访问响应的 `body` 属性：

```js
// Fetch the original image
fetch("./tortoise.png")
  // Retrieve its body as ReadableStream
  .then((response) => response.body);
```

这为我们提供了 `ReadableStream` 对象。

### 附着一个 reader

现在我们已经获取到我们流的主体，读取流需要给它附着一个 reader。使用 `ReadableStream.getReader()` 方法完成：

```js
// Fetch the original image
fetch("./tortoise.png")
  // Retrieve its body as ReadableStream
  .then((response) => response.body)
  .then((body) => {
    const reader = body.getReader();
    // …
  });
```

调用这个方法创建一个 reader 并且用它锁定这个流——直到释放这个 reader（即通过调用 `ReadableStreamDefaultReader.releaseLock()`），没有其他 reader 能读这个流。

另请注意，先前的示例可以减少一步，由于 `response.body` 是同步的，所以不需要 promise：

```js
// Fetch the original image
fetch("./tortoise.png")
  // Retrieve its body as ReadableStream
  .then((response) => {
    const reader = response.body.getReader();
    // …
  });
```

### 读取流

现在你已经附着了你的 reader，你可以使用 `ReadableStreamDefaultReader.read()` 方法从流中读取数据分块。你从流中读出分块后，可以做你喜欢的事。例如，我们的简单流式读取示例将分块送入新的自定义 `ReadableStream` 中（我们将在下一节发现更多信息），然后从中创建一个新的响应，将它作为 `Blob` 使用，然后通过 `URL.createObjectURL()` 从该 blob 创建一个对象 URL，并将其显示在屏幕上的 `<img>` 元素中，有效地创建了我们最初获取的图像的副本。

```js
// Fetch the original image
fetch("./tortoise.png")
  // Retrieve its body as ReadableStream
  .then((response) => {
    const reader = response.body.getReader();
    return new ReadableStream({
      start(controller) {
        return pump();
        function pump() {
          return reader.read().then(({ done, value }) => {
            // When no more data needs to be consumed, close the stream
            if (done) {
              controller.close();
              return;
            }
            // Enqueue the next data chunk into our target stream
            controller.enqueue(value);
            return pump();
          });
        }
      },
    });
  })
  // Create a new response out of the stream
  .then((stream) => new Response(stream))
  // Create an object URL for the response
  .then((response) => response.blob())
  .then((blob) => URL.createObjectURL(blob))
  // Update image
  .then((url) => console.log((image.src = url)))
  .catch((err) => console.error(err));
```

让我们详细看看如何使用 `read()`。在 `pump()` 函数中，我们首先调用 `read()`，其返回一个包含对象的 promise——这里有我们要读去的结果，其形式为 `{ done, value }`：

```js
reader.read().then(({ done, value }) => {
  /\* … \*/
});
```

这个结果可能是三种不同的类型之一：

* 如果有分块可用，则 promise 将使用 `{ value: theChunk, done: false }` 形式的对象来兑现。
* 如果流已经关闭，则 promise 将使用 `{ value: undefined, done: true }` 形式的对象来兑现。
* 如果流发生错误，则 promise 将因相关错误被拒绝。

其次，我们检查 `done` 是否为 `true`。如果是，则没有更多的分块要读取（value 的值是 `undefined`），所以我们退出这个函数并且使用 `ReadableStreamDefaultController.close()` 关闭自定义的流：

```js
if (done) {
  controller.close();
  return;
}
```

**备注：** `close()` 是新自定义流的一部分，而不是我们在这个讨论的一部分。我们将在下一节阐述更多关于自定义流的内容。

如果 `done` 为 `true`，我们处理已经读取的新分块（包含在结果对象的 `value` 属性），然后再次调用 `pump()` 函数去读取下一个分块。

```js
// Enqueue the next data chunk into our target stream
controller.enqueue(value);
return pump();
```

这是当你在使用流的 reader 时，将看见的标准的模式：

1. 编写一个从流的读取开始的函数。
2. 如果流中没有更多的分块要读取，你需要退出这个函数。
3. 如果流中有更多的分块要读取，你可以处理当前的分块后，再次运行该函数。
4. 你继续链接 `pipe` 函数，直到没有更多流要读取，在这种情况下，请遵循步骤 2。

**备注：** 该函数看起来像 `pump()` 调用自己并且导致一个潜在的深度递归。然而，因为 `pump` 是异步的并且每次调用 `pump()` 都是在 promise 处理程序的末尾，事实上，它类似于 promise 处理程序的链式结构。

创建你自定义的可读流
----------

我们在本文中一直在研究简单流式读取示例，包括第二部分——一旦我们从 fetch 主体中以分块的形式读取图片，我们就可以将它们排入另一个我们自定义的流中。我们该如何创建 `ReadableStream()` 构造函数？

### ReadableStream() 构造函数

当浏览器为你提供流时，可以很容易的读取，正如 Fetch 的情况一样，但是有时候你需要创建一个自定义流并且用你自己的分块填充它。`ReadableStream()` 构造函数允许你通过最初看起来很复杂的语法创建它，但是这确实不是最糟糕的。

通用的语法框架像这样：

```js
const stream = new ReadableStream(
  {
    start(controller) {},
    pull(controller) {},
    cancel() {},
    type,
    autoAllocateChunkSize,
  },
  {
    highWaterMark: 3,
    size: () => 1,
  },
);
```

构造函数需要两个对象作为参数。第一个对象时必需的，并在 JavaScript 中创建一个正在读取数据的底层源模型。第二个对象是可选的，并且允许你去指定一个自定义的队列策略用于自己的流。你将很少这么做，所以我们现在只要专注于第一个。

第一个对象包含着五个成员，仅有第一个是必要的：

1. `start(controller)`——一个在 `ReadableStream` 构建后，立即被调用一次的方法。在这个方法中，你应该包含设置流功能的代码，例如开始生成数据或者以其他的方式访问资源时。
2. `pull(controller)`——一个方法，当被包含时，它会被重复的调用直到填满流的内置队列。当排入更多的分块时，这可以用于控制流。
3. `cancel()`——一个方法，当被包含时，如果应用发出流将被取消的信号，它将被调用（例如，调用 `ReadableStream.cancel()`）。内容应该采取任何必要的措施释放对流源的访问。
4. `type` 和 `autoAllocateChunkSize`——当它们被包含时，会被用来表示流将是一个字节流。字节流将在未来的教程中单独涵盖，因为它们在目的和用例上与常规的（默认的）流有些不同。它们也未在任何地方实施。

再次看我们的简单示例代码，你可以看见我们的构造函数 `ReadableStream()` 仅包含一个单独的方法——`start()`，它用于读取我们 fetch 流中的所有的数据。

```js
// Fetch the original image
fetch("./tortoise.png")
  // Retrieve its body as ReadableStream
  .then((response) => {
    const reader = response.body.getReader();
    return new ReadableStream({
      start(controller) {
        return pump();
        function pump() {
          return reader.read().then(({ done, value }) => {
            // When no more data needs to be consumed, close the stream
            if (done) {
              controller.close();
              return;
            }
            // Enqueue the next data chunk into our target stream
            controller.enqueue(value);
            return pump();
          });
        }
      },
    });
  });
```

### ReadableStream controller

你将注意到传递给 `ReadableStream` 构造函数的 `start()` 和 `pull()` 方法指定了 `controller` 参数——这些是 `ReadableStreamDefaultController` 类的实例，它可以用于控制你的流。

在我们的示例中，当读取 fetch 主体后，使用 controller 的 `enqueue()` 方法去将 value 排入自定义的流中。

另外，当我们完成读取 fetch 主体，我们使用 controller 的 `close()` 方法去关闭自定义流——任何先前排入的分块仍然可以从中读取，但是不会排入更多的分块，并且当读取结束时流被关闭。

### 读取自定义流

在我们的简单流式读取示例中，我们将其传递到 `Response` 构造函数中来消费自定义的可读流，然后我们将它作为 `blob()` 来使用。

```js
readableStream
  .then((stream) => new Response(stream))
  .then((response) => response.blob())
  .then((blob) => URL.createObjectURL(blob))
  .then((url) => console.log((image.src = url)))
  .catch((err) => console.error(err));
```

但是一个自定义流仍然是 `ReadableStream` 实例，意味着你可以给它附着一个 reader。例如，看看我们的简单随机流示例（也可以参见在线演示），其创建了一个自定义的流，排入了一些随机的字符串，然后在按下 *Stop string generation* 的按钮后，再次从流中读取数据。

**备注：** 为了使用 `FetchEvent.respondWith()` 消费流，排入的流内容的类型必须是 `Uint8Array`；例如使用 `TextEncoder` 进行编码。

自定义流的构造函数有一个 `start()` 方法，该方法使用 `setInterval()` 去指定每秒生成一个随机的字符串。然后使用 `ReadableStreamDefaultController.enqueue()` 将它排入流。当按下按钮，取消 interval，并调用名为 `readStream()` 函数再次将数据从流中读取回来。由于我们一直停止排入分块，所以我们也要关闭流。

```js
const stream = new ReadableStream({
  start(controller) {
    interval = setInterval(() => {
      const string = randomChars();
      // Add the string to the stream
      controller.enqueue(string);
      // show it on the screen
      const listItem = document.createElement("li");
      listItem.textContent = string;
      list1.appendChild(listItem);
    }, 1000);
    button.addEventListener("click", () => {
      clearInterval(interval);
      readStream();
      controller.close();
    });
  },
  pull(controller) {
    // We don't really need a pull in this example
  },
  cancel() {
    // This is called if the reader cancels,
    // so we should stop generating strings
    clearInterval(interval);
  },
});
```

在 `readStream()` 函数中，我们使用 `ReadableStream.getReader()` 将 reader 锁定到该流，然后遵循我们之前看到的相同的模式——使用 `reader` 读取每个分块，在再次运行 `read()` 方法之前，检查 `done` 是否为 `true`，如果是 true，处理结束，如果是 false，读取下一个分块并且处理它。

```js
function readStream() {
  const reader = stream.getReader();
  let charsReceived = 0;
  let result = "";

  // read() returns a promise that resolves
  // when a value has been received
  reader.read().then(function processText({ done, value }) {
    // Result objects contain two properties:
    // done - true if the stream has already given you all its data.
    // value - some data. Always undefined when done is true.
    if (done) {
      console.log("Stream complete");
      para.textContent = result;
      return;
    }

    charsReceived += value.length;
    const chunk = value;
    const listItem = document.createElement("li");
    listItem.textContent = `Read ${charsReceived} characters so far. Current chunk = ${chunk}`;
    list2.appendChild(listItem);

    result += chunk;

    // Read some more, and call this function again
    return reader.read().then(processText);
  });
}
```

### 关闭并取消流

我们已经展示了使用 `ReadableStreamDefaultController.close()` 去关闭 reader 的示例。正如我们之前说的那样，任意排入队的分块将仍然被读取，但是因为它被关闭了，不会再有更多的分块入队。

如果你想要完全的摆脱流并且丢弃所有入队的分块，你应该使用 `ReadableStream.cancel()` 或 `ReadableStreamDefaultReader.cancel()`。

拷贝流
---

有时候你可能想要同时读取两次流。该过程由调用 `ReadableStream.tee()` 实现——它返回一个数组，包含对原始可读流的两个相同的副本可读流，然后可以独立的使用不同的 reader 读取。

举例而言，你在 ServiceWorker 中可能会用到该方法，当你从服务器 fetch 资源，得到一个响应的可读流，你可能会想把这个流拆分成两个，一个流入到浏览器，另一个流入到 ServiceWorker 的缓存。由于响应的主体无法被消费两次，以及可读流无法被两个 reader 同时读取，你会需要两个可读流副本来实现需求。

我们提供了一个示例，在我们的简单拷贝示例（也可以参见在线演示）。这个示例与我们的简单随机流示例的工作方式大致相同，只是当按钮按下停止生产随机字符串时，将采取自定义流并拷贝流，并且读取这两个生成的流：

```js
function teeStream() {
  const teedOff = stream.tee();
  readStream(teedOff[0], list2);
  readStream(teedOff[1], list3);
}
```

链式管道传输
------

流的另一特征是通过管道的方式从一个流输出到另一个（称为链式管道传输）。这会调用两个方法——`ReadableStream.pipeThrough()`，它将可读流管道输出至拥有一对 writer/reader 的流中，并将一种数据转换成另一种；`ReadableStream.pipeTo()` 将可读流管道传输至作为链式管道传输终点的 writer。

我们有一个简单的示例，叫做解压 PNG 分块（也可以参见在线演示）。此示例将图像作为流来获取，然后将它传输到自定义的 PNG 转换流，该流将从二进制数据流中检索 PNG 分块。

```js
// Fetch the original image
fetch("png-logo.png")
  // Retrieve its body as ReadableStream
  .then((response) => response.body)
  // Create a gray-scaled PNG stream out of the original
  .then((rs) => logReadableStream("Fetch Response Stream", rs))
  .then((body) => body.pipeThrough(new PNGTransformStream()))
  .then((rs) => logReadableStream("PNG Chunk Stream", rs));
```

我们仍然没有使用 `TransformStream` 的例子。

总结
--

这解释了“默认”可读流的知识。

关于如何使用可读*字节*流的信息，参见使用可读字节流 (en-US)：具有底层字节源的流，可以向消费者执行高效的零复制传输，绕过流的内部队列。


Stream API 概念 - Web API 接口参考

Stream API 概念
=============

Stream API 为 Web 平台提供了一组十分有用的工具，提供了一系列对象以允许 JavaScript 访问来自网络的数据流，并根据开发人员的需要对其进行处理。与流相关的一些概念和术语可能会令你感到陌生——本文将解释你需要了解的所有内容。

可读流
---

一个可读流是一个数据源，在 JavaScript 中用一个 `ReadableStream` 对象表示，数据从它的**底层源**（underlying source）流出——底层源表示一个你希望从中获取数据，且来自网络或其他域的某种资源。

有两种类型的底层源：

* **Push source** 会在你访问了它们之后，不断地主动推送数据。你可以自行开始（start）、暂停（pause）或取消（cancel）对流的访问。例如视频流和 TCP/Web socket。
* **Pull source** 需要在你连接到它们后，显式地请求数据。例如通过 Fetch 或 XHR 请求访问一个文件。

数据被按序读入到许多小的片段，这些片段被称作**分块**（chunk）。**分块**可以是单个字节，也可以是某种更大的数据类型，例如特定大小的类型化数组。单个流的**分块**可以有不同的大小和类型。

![](/zh-CN/docs/Web/API/Streams_API/Concepts/readable_streams.png)

已放入到流中的分块称作**已入队**（enqueued）——这意味着它们已经在队列中排队等待被读取。流的一个**内置队列**跟踪了那些尚未读取的分块（请参阅下面的**内部队列和队列策略**部分）。

流中的分块由一个 **reader** 读取——该数据处理过程一次只处理一个分块，允许你对其执行任何类型的操作。reader 加上与它一起运行的其他处理代码合称为一个 **consumer**。

另一个你将用到的对象叫做 **controller**——每个 reader 都有一个关联的 controller，用来控制流（例如，可以将流关闭）。

一个流一次只能被一个 reader 读取；当一个 reader 被创建并开始读一个流（一个活跃的 reader）时，我们说，它被**锁定**（locked）在该流上。如果你想让另一个 reader 读这个流，则通常需要先取消第一个 reader，再执行其他操作（你也可以**拷贝**该流，请参阅下面的拷贝部分）。

注意，有两种不同类型的可读流。除了传统的可读流之外，还有一种类型叫做字节流——这是传统流的扩展版本，用于读取底层字节源。相比于传统的可读流，字节流被允许通过 BYOB reader 读取（BYOB，“带上你自己的缓冲区”）。这种 reader 可以直接将流读入开发者提供的缓冲区，从而最大限度地减少所需的复制。你的代码将使用哪种底层流（以及使用哪种 reader 和 controller）取决于流最初是如何创建的（请参阅 `ReadableStream()` 构造函数页面）。

你可以使用现成的可读流，例如来自 fetch 请求的 `Response.body`，也可以使用 `ReadableStream()` 构造函数生成自定义的流。

拷贝（teeing）
----------

尽管同一时刻只能有一个 reader 可以读取流，我们可以把流分割成两个相同的副本，这样它们就可以用两个独立的 reader 读取。该过程称为**拷贝**。

在 JavaScript 中，该过程由调用 `ReadableStream.tee()` 实现——它返回一个数组，包含对原始可读流的两个相同的副本可读流，然后可以独立的使用不同的 reader 读取。

举例而言，你在 ServiceWorker 中可能会用到该方法，当你从服务器 fetch 资源，得到一个响应的可读流，你可能会想把这个流拆分成两个，一个流入到浏览器，另一个流入到 ServiceWorker 的缓存。由于响应的主体无法被消费两次，以及可读流无法被两个 reader 同时读取，你会需要两个可读流副本来实现需求。

![拷贝数据流](/zh-CN/docs/Web/API/Streams_API/Concepts/tee.png)

可写流
---

一个**可写流**（Writable stream）是一个可以写入数据的数据终点，在 JavaScript 中以一个 `WritableStream` 对象表示。这是 JavaScript 层面对**底层接收器**（underlying sink）的抽象——一个更低层次的 I/O 接收器，将原始数据写入其中。

数据由一个 **writer** 写入流中，每次只写入一个分块。分块和可读流的 reader 一样可以有多种类型。你可以用任何方式生成要被写入的块；writer 加上相关的代码称为**生产者**。

当 writer 被创建并开始向一个流写入数据（一个活跃的 writer）时，我们说，它被**锁定**（locked）在该流上。同一时刻，一个 writer 只能向一个可写流写入数据。如果你想要用其他 writer 向流中写入数据，在你将 writer 附着到该流之前，你必须先中止上一个 writer。

一个**内置队列**跟踪已经被写入流的分块但是仍然没有被底层接收器处理的分块。

另一个你将用到的对象叫做 controller——每个 writer 都有一个关联的 controller，用来控制流（例如，可以将流关闭）。

![可写流数据流向](/zh-CN/docs/Web/API/Streams_API/Concepts/writable_streams.png)

你可以使用 `WritableStream()` 构造函数使用可写流。这些目前在浏览器中的应用非常有限。

链式管道传输
------

Streams API 使用**链式管道**（pipe chain）的结构将流传输到另一个流已经成为可能。有两种方法可以作用于它：

* `ReadableStream.pipeThrough()`——通过**转换流**（transform stream）传输流，可能在传输过程中转换流。例如，他可以用于将编码或解码视频帧、压缩或解压缩数据或以其他的方式从一种数据转换成另一种数据。一个转换流由一对流组成：一个读取数据的可读流和一个写入数据的可写流，它们以适当的机制确保新数据一旦写入后即可读取。`TransformStream` 是转换流的具体实现，但任意具有相同可读流和可写流属性的对象都可以传递给 `pipeThrough()`。
* `ReadableStream.pipeTo()`——传输到可写流，并且作为链式管道传输的终点。

链式管道传输的起点称为**原始源**（original source），终点称为**最终接收器**（ultimate sink）。

![链式管道传输](/zh-CN/docs/Web/API/Streams_API/Concepts/pipechain.png)

背压（backpressure）
----------------

流的一个重要概念是**背压**——这是单个流或一个链式管道调节读/写速度的过程。当链中后面的一个流仍然忙碌，尚未准备好接受更多的分块时，它会通过链向上游的流发送一个信号，告诉上游的转换流（或原始源）适当地减慢传输速度，这样你就不会在任何地方遇到瓶颈。

要在可读流中使用背压技术，我们可以通过查询 controller 的 `ReadableStreamDefaultController.desiredSize` 属性。如果该值太低或为负数，我们的 ReadableStream 可以告诉它的底层源停止往流中装载数据，然后我们沿着 stream chain 进行背压。

可读流在经历背压后，如果消费者再次想要接收数据，我们可以在构造可读流时提供 pull 方法来告诉底层源恢复往流中装载数据。

内置队列和队列策略
---------

正如最初提到的，在流中一直没有处理和完成的分块由一个内置的队列跟踪。

* 就可读流而言，这些分块一直在排队，但仍然没有被读取。
* 就写入流而言，这些分块一直被写入，但仍然没有通过底层接收器处理。

内置的队列采用一个**队列策略**，该策略规定如何基于**内置队列的状态**发出背压信号。

一般来说，该策略会将队列中的分块大小与称作 **high water mark** 的值进行比较，**high water mark** 是队列期望管理的最大分块的总和。

执行的计算是：

`high water mark - total size of chunks in queue = desired size`

**所需大小**（desired size）是流中仍然可以接收的分块数量，以保持流运行，但是小于 high water mark 的大小。当所需的大小大于 0 时，分块的生成将适当的减慢或者加速，以保持流尽可能快的运行。如果值降到 0（或者小于 0），这意味着分块的产生快于流的处理，这可能产生问题。

举一个例子，让我们为 1 的分块大小 和为 3 的 high water mark 为例：这意味着在达到 high water mark 和运用背压之前，最多可以入队 3 个分块。


AudioNode.connect() - Web API 接口参考

AudioNode.connect()
===================

`AudioNode` 接口的 `connect()` 方法使你能将一个节点的输出连接到一个指定目标，这个指定的目标可能是另一个 `AudioNode`（从而将音频数据引导到下一个指定节点）或一个`AudioParam`, 以便上一个节点的输出数据随着时间流逝能自动地对下一个参数值进行改变。

语法
--
```

var destinationNode = AudioNode.connect(destination, outputIndex, inputIndex);

AudioNode.connect(destination, outputIndex);
```

### 属性

`destination`

需要连接的 `AudioNode` 或 `AudioParam`.

`outputIndex` 可选

一个索引，用于描述当前 `AudioNode` 的哪个输出会连接到 destination。索引数字是由输出频道（详见 Audio channels）的数量来确定的。当你只能将给定的输出连接到给定的输入一次（重复的尝试会被忽略）时，可以通过多次调用 `connect()` 将一个输出连接到多个输入。可以通过这样来实现扇出。这个参数的默认值为 0。

`inputIndex` 可选

一个索引，用于描述当前 `AudioNode` 会连接到 destination 的哪个输入，它的默认值是 0。索引数字是由输入频道（详见 Audio channels）的数量来确定的。将一个 `AudioNode` 连接回之前的 `AudioNode`，以此形成一个圈是可行的。不过只在这个圈里有至少一个 `DelayNode` (en-US) 才可行。否则会抛出一个 `NotSupportedError` 异常。此参数在 destination 是 `AudioParam`时不可用。

### 返回值

如果 destination 是一个节点， `connect()` 返回 destination 所表示的 `AudioNode` 对象的引用，允许你链式地调用数个 `connect()` 。某些浏览器关于该接口的旧实现会返回 `undefined`。

如果 destination 是一个 `AudioParam`、`connect()` 返回 `undefined`。

### 异常

`IndexSizeError`

这个异常表明 `outputIndex` 或 `inputIndex` 与当前输入或输出不符。

`InvalidAccessError`

目标节点与原节点不在同一个音频上下文。

`NotSupportedError`

该链接会形成一个闭环（音频在这个环里不断重复经过同一个节点）并且这个闭环里没有 `DelayNode` (en-US) 来防止产生的波形被卡住，不停地构建相同的音频帧。

示例
--

### Connecting to an audio input

The most obvious use of the `connect()` method is to direct the audio output from one node into the audio input of another node for further processing. For example, you might send the audio from a `MediaElementAudioSourceNode`—that is, the audio from an HTML5 media element such as `<audio>`—through a band pass filter implemented using a `BiquadFilterNode` to reduce noise before then sending the audio along to the speakers.

This example creates an oscillator, then links it to a gain node, so that the gain node controls the volume of the oscillator node.

```js
var AudioContext = window.AudioContext || window.webkitAudioContext;

var audioCtx = new AudioContext();

var oscillator = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();

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

### AudioParam example

In this example, we will be altering the gain value of a `GainNode` using an `OscillatorNode` with a slow frequency value. This technique is know as an *LFO*-controlled parameter.

```js
var AudioContext = window.AudioContext || window.webkitAudioContext;

var audioCtx = new AudioContext();

// create an normal oscillator to make sound
var oscillator = audioCtx.createOscillator();

// create a second oscillator that will be used as an LFO (Low-frequency
// oscillator), and will control a parameter
var lfo = audioCtx.createOscillator();

// set the frequency of the second oscillator to a low number
lfo.frequency.value = 2.0; // 2Hz: two oscillations par second

// create a gain whose gain AudioParam will be controlled by the LFO
var gain = audioCtx.createGain();

// connect the LFO to the gain AudioParam. This means the value of the LFO
// will not produce any audio, but will change the value of the gain instead
lfo.connect(gain.gain);

// connect the oscillator that will produce audio to the gain
oscillator.connect(gain);

// connect the gain to the destination so we hear sound
gain.connect(audioCtx.destination);

// start the oscillator that will produce audio
oscillator.start();

// start the oscillator that will modify the gain value
lfo.start();
```

#### AudioParam notes

It is possible to connect an `AudioNode` output to more than one `AudioParam`, and more than one AudioNode output to a single `AudioParam`, with multiple calls to `connect()`. Fan-in and fan-out are therefore supported.

An `AudioParam` will take the rendered audio data from any `AudioNode` output connected to it and convert it to mono by down-mixing (if it is not already mono). Next, it will mix it together with any other such outputs, and the intrinsic parameter value (the value the `AudioParam` would normally have without any audio connections), including any timeline changes scheduled for the parameter.

Therefore, it is possible to choose the range in which an `AudioParam` will change by setting the value of the `AudioParam` to the central frequency, and to use a `GainNode` between the audio source and the `AudioParam` to adjust the range of the `AudioParam` changes.

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

| Specification |
| --- |
| Web Audio API # dom-audionode-connect |
| Web Audio API # dom-audionode-connect-destinationparam-output |

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

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

See also
--------

* Using the Web Audio API


在 JavaScript 中通过 queueMicrotask() 使用微任务 - Web API 接口参考

在 JavaScript 中通过 queueMicrotask() 使用微任务
=======================================

一个**微任务**（microtask）就是一个简短的函数，当创建该微任务的函数执行之后，*并且*只有当 Javascript 调用栈为空，而控制权尚未返还给被用户代理用来驱动脚本执行环境的事件循环之前，该微任务才会被执行。事件循环既可能是浏览器的主事件循环也可能是被一个 web worker 所驱动的事件循环。这使得给定的函数在没有其他脚本执行干扰的情况下运行，也保证了微任务能在用户代理有机会对该微任务带来的行为做出反应之前运行。

JavaScript 中的 promise 和 Mutation Observer API 都使用微任务队列去运行它们的回调函数，但当能够推迟工作直到当前事件循环过程完结时，也是可以执行微任务的时机。为了允许第三方库、框架、polyfill 能使用微任务，在 `Window` 和 `Worker` 接口上暴露了 `queueMicrotask()` 方法。

任务 vs 微任务
---------

为了正确地讨论微任务，首先最好知道什么是一个 JavaScript 任务以及微任务如何区别于任务。这里是一个快速、简单的解释，但若你想了解更多细节，可以阅读这篇文章中的信息 In depth: Microtasks and the JavaScript runtime environment.

### 任务（Tasks）

一个**任务**就是由执行诸如从头执行一段程序、执行一个事件回调或一个 interval/timeout 被触发之类的标准机制而被调度的任意 JavaScript 代码。这些都在**任务队列**（task queue）上被调度。

在以下时机，任务会被添加到任务队列：

* 一段新程序或子程序被直接执行时（比如从一个控制台，或在一个 `<script>` 元素中运行代码）。
* 触发了一个事件，将其回调函数添加到任务队列时。
* 执行到一个由 `setTimeout()` 或 `setInterval()` 创建的 timeout 或 interval，以致相应的回调函数被添加到任务队列时。

事件循环驱动你的代码按照这些任务排队的顺序，一个接一个地处理它们。在事件循环的单次迭代中，将执行任务队列中最旧的可运行任务。之后，微任务将被执行，直到微任务队列为空，然后浏览器可以选择更新渲染。然后浏览器继续进行事件循环的下一次迭代。

### 微任务（Microtasks）

起初微任务和任务之间的差异看起来不大。它们很相似；都由位于某个队列的 JavaScript 代码组成并在合适的时候运行。但是，只有在迭代开始时队列中存在的任务才会被事件循环一个接一个地运行，这和处理微任务队列是殊为不同的。

有两点关键的区别。

首先，每当一个任务存在，事件循环都会检查该任务是否正把控制权交给其他 JavaScript 代码。如若不然，事件循环就会运行微任务队列中的所有微任务。接下来微任务循环会在事件循环的每次迭代中被处理多次，包括处理完事件和其他回调之后。

其次，如果一个微任务通过调用 `queueMicrotask()`, 向队列中加入了更多的微任务，则那些新加入的微任务 *会早于下一个任务运行*。这是因为事件循环会持续调用微任务直至队列中没有留存的，即使是在有更多微任务持续被加入的情况下。

**警告：** 因为微任务自身可以入列更多的微任务，且事件循环会持续处理微任务直至队列为空，那么就存在一种使得事件循环无尽处理微任务的真实风险。如何处理递归增加微任务是要谨慎而行的。

使用微任务
-----

在谈论更多之前，再次注意到一点是重要的，那就是如果可能的话，大部分开发者并不应该过多的使用微任务。在基于现代浏览器的 JavaScript 开发中有一个高度专业化的特性，那就是允许你调度代码跳转到其他事情之前，而那些事情原本是处于用户计算机中一大堆等待发生的事情集合之中的。滥用这种能力将带来性能问题。

### 入列微任务

就其本身而言，应该使用微任务的典型情况，要么只有在没有其他办法的时候，要么是当创建框架或库时需要使用微任务达成其功能。虽然在过去要使得入列微任务成为可能有可用的技巧（比如创建一个立即 resolve 的 promise），但新加入的 `queueMicrotask()` 方法增加了一种标准的方式，可以安全的引入微任务而避免使用额外的技巧。

通过引入 `queueMicrotask()`，由晦涩地使用 promise 去创建微任务而带来的风险就可以被避免了。举例来说，当使用 promise 创建微任务时，由回调抛出的异常被报告为 rejected promises 而不是标准异常。同时，创建和销毁 promise 带来了事件和内存方面的额外开销，这是正确入列微任务的函数应该避免的。

简单的传入一个 JavaScript `Function` ，以在 `queueMicrotask()` 方法中处理微任务时供其上下文调用即可；取决于当前执行上下文， `queueMicrotask()` 以定义的形式被暴露在 `Window` 或 `Worker` 接口上。

```js
queueMicrotask(() => {
  /\* 微任务中将运行的代码 \*/
});
```

微任务函数本身没有参数，也不返回值。

### 何时使用微任务

在本章节中，我们来看看微任务特别有用的场景。通常，这些场景关乎捕捉或检查结果、执行清理等；其时机晚于一段 JavaScript 执行上下文主体的退出，但早于任何事件处理函数、timeouts 或 intervals 及其他回调被执行。

何时是那种有用的时候？

使用微任务的最主要原因简单归纳为：确保任务顺序的一致性，即便当结果或数据是同步可用的，也要同时减少操作中用户可感知到的延迟而带来的风险。

#### 保证条件性使用 promises 时的顺序

微任务可被用来确保执行顺序总是一致的一种情形，是当 promise 被用在一个 `if...else` 语句（或其他条件性语句）中、但并不在其他子句中的时候。考虑如下代码：

```js
customElement.prototype.getData = url => {
  if (this.cache[url]) {
    this.data = this.cache[url];
    this.dispatchEvent(new Event("load"));
  } else {
    fetch(url).then(result => result.arrayBuffer()).then(data => {
      this.cache[url] = data;
      this.data = data;
      this.dispatchEvent(new Event("load"));
    )};
  }
};
```

这段代码带来的问题是，通过在 `if...else` 语句的其中一个分支（此例中为缓存中的图片地址可用时）中使用一个任务而 promise 包含在 `else` 子句中，我们面临了操作顺序可能不同的局势；比方说，像下面看起来的这样：

```js
element.addEventListener("load", () => console.log("Loaded data"));
console.log("Fetching data...");
element.getData();
console.log("Data fetched");
```

连续执行两次这段代码会形成下表中的结果：

数据未缓存的结果（左）vs. 缓存中有数据的结果| 数据未缓存 | 数据已缓存 |
| --- | --- |
|
```
Fetching data
Data fetched
Loaded data
```

 |
```
Fetching data
Loaded data
Data fetched
```

 |

甚至更糟的是，有时元素的 `data`

属性会被设置，还有时当这段代码结束运行时却不会被设置。

我们可以通过在 `if` 子句里使用一个微任务来确保操作顺序的一致性，以达到平衡两个子句的目的：

```js
customElement.prototype.getData = url => {
  if (this.cache[url]) {
    queueMicrotask(() => {
      this.data = this.cache[url];
      this.dispatchEvent(new Event("load"));
    });
  } else {
    fetch(url).then(result => result.arrayBuffer()).then(data => {
      this.cache[url] = data;
      this.data = data;
      this.dispatchEvent(new Event("load"));
    )};
  }
};
```

通过在两种情况下各自都通过一个微任务（`if` 中用的是 `queueMicrotask()` 而 `else` 子句中通过 `fetch()` 使用了 promise）处理了设置 `data` 和触发 `load` 事件，平衡了两个子句。

#### 批量操作

也可以使用微任务从不同来源将多个请求收集到单一的批处理中，从而避免对处理同类工作的多次调用可能造成的开销。

下面的代码片段创建了一个函数，将多个消息放入一个数组中批处理，通过一个微任务在上下文退出时将这些消息作为单一的对象发送出去。

```js
const messageQueue = [];

let sendMessage = (message) => {
  messageQueue.push(message);

  if (messageQueue.length === 1) {
    queueMicrotask(() => {
      const json = JSON.stringify(messageQueue);
      messageQueue.length = 0;
      fetch("url-of-receiver", json);
    });
  }
};
```

当 `sendMessage()`

被调用时，指定的消息首先被推入消息队列数组。接着事情就变得有趣了。

如果我们刚加入数组的消息是第一条，就入列一个将会发送一个批处理的微任务。照旧，当 JavaScript 执行路径到达顶层，恰在运行回调之前，那个微任务将会执行。这意味着之后的间歇期内造成的对 `sendMessage()` 的任何调用都会将其各自的消息推入消息队列，但囿于入列微任务逻辑之前的数组长度检查，不会有新的微任务入列。

当微任务运行之时，等待它处理的可能是一个有若干条消息的数组。微任务函数先是通过 `JSON.stringify()` 方法将消息数组编码为 JSON。其后，数组中的内容就不再需要了，所以清空 `messageQueue` 数组。最后，使用 `fetch()` 方法将编码后的 JSON 发往服务器。

这使得同一次事件循环迭代期间发生的每次 `sendMessage()` 调用将其消息添加到同一个 `fetch()` 操作中，而不会让诸如 timeouts 等其他可能的定时任务推迟传递。

服务器将接到 JSON 字符串，然后大概会将其解码并处理其从结果数组中找到的消息。

例子
--

### 简单微任务示例

在这个简单的例子中，我们将看到入列一个微任务后，会引起其回调函数在顶层脚本完毕后运行。
```

<pre id="log"></pre>
```

#### JavaScript
```

let logElem = document.getElementById("log");
let log = (s) => (logElem.innerHTML += s + "<br>");
```

在下面的代码中，我们看到对 `queueMicrotask()` 的一次调用被用来调度一个微任务以使其运行。这次调用包含了 `log()`，一个简单的向屏幕输出文字的自定义函数。

```js
log("Before enqueueing the microtask");
queueMicrotask(() => {
  log("The microtask has run.");
});
log("After enqueueing the microtask");
```

#### 结果

### timeout 和微任务的示例

在这个例子中，一个 timeout 在 0 毫秒后被触发（或者 "尽可能快"）。这演示了当调用一个新任务（如通过使用 `setTimeout()`）时的“尽可能快”意味着什么，以及比之于使用一个微任务的不同。
```

<pre id="log"></pre>
```

#### JavaScript
```

let logElem = document.getElementById("log");
let log = (s) => (logElem.innerHTML += s + "<br>");
```

在下面的代码中，我们看到对 `queueMicrotask()` 的一次调用被用来调度一个微任务以使其运行。这次调用包含了 `log()`，一个简单的向屏幕输出文字的自定义函数。

以下代码调度了一个 0 毫秒后触发的 timeout，而后入列了一个微任务。前后被对 `log()` 的调用包住，输出附加的信息。

```js
let callback = () => log("Regular timeout callback has run");

let urgentCallback = () => log("\*\*\* Oh noes! An urgent callback has run!");

log("Main program started");
setTimeout(callback, 0);
queueMicrotask(urgentCallback);
log("Main program exiting");
```

#### 结果

可以注意到，从主程序体中输出的日志首先出现，接下来是微任务中的输出，其后是 timeout 的回调。这是因为当处理主程序运行的任务退出后，微任务队列先于 timeout 回调所在的任务队列被处理。要记住任务和微任务是保持各自独立的队列的，且微任务先执行有助于保持这一点。

### 来自函数的微任务

这个例子通过增加一个完成同样工作的函数，略微地扩展了前一个例子。该函数使用 `queueMicrotask()` 调度一个微任务。此例的重要之处是微任务不在其所处的函数退出时，而是在主程序退出时被执行。
```

<pre id="log"></pre>
```

#### JavaScript
```

let logElem = document.getElementById("log");
let log = (s) => (logElem.innerHTML += s + "<br>");
```

以下是主程序代码。这里的 `doWork()` 函数调用了 `queueMicrotask()`，但微任务仍在整个程序退出时才触发，因为那才是任务退出而执行栈上为空的时刻。

```js
let callback = () => log("Regular timeout callback has run");

let urgentCallback = () => log("\*\*\* Oh noes! An urgent callback has run!");

let doWork = () => {
  let result = 1;

  queueMicrotask(urgentCallback);

  for (let i = 2; i <= 10; i++) {
    result \*= i;
  }
  return result;
};

log("Main program started");
setTimeout(callback, 0);
log(`10! equals ${doWork()}`);
log("Main program exiting");
```

#### 结果

参见
--

* In depth: Microtasks and the JavaScript runtime environment
* `queueMicrotask()`
* Asynchronous JavaScript
    + General asynchronous programming concepts
    + Introducing asynchronous JavaScript
    + Cooperative asynchronous JavaScript: Timeouts and intervals
    + Graceful asynchronous programming with Promises
    + Choosing the right approach


深入：微任务与 Javascript 运行时环境 - Web API 接口参考

深入：微任务与 Javascript 运行时环境
========================

当你在调试，或者在决定如何以最佳的方式为任务队列和微任务队列安排调度顺序的时候，如果你了解关于 JavaScript 运行时是如何在背后执行这一切的，那将对你的理解会非常有帮助。

JavaScript 本质上是一门单线程语言。对于在它被设计出来的那个年代来说，这样的设计是一个很好的选择。那个时候的人们很少有多处理器计算机，而且当时预期由 JavaScript 处理的代码量也相对较少。

当然，随着时间的流逝，计算机已经发展成为强大的多核系统，而 JavaScript 已经成为计算世界中使用最广泛的语言之一。大量最流行的应用程序至少有一部分是基于 JavaScript 代码的。为了支持这一点，有必要找到方法让项目摆脱单线程语言的限制。

自从定时器（`setTimeout()` 和 `setInterval()`）加入到 Web API 后，浏览器提供的 JavaScript 环境就已经逐渐发展到包含任务调度、多线程应用开发等强大的特性。了解 JavaScript 运行时是如何安排和运行代码的对了解 `queueMicrotask()` 会非常有作用。

JavaScript 执行上下文
----------------

**备注：** 对于大多数 JavaScript 开发人员来说，这些细节并不重要。这里提供的信息只用于了解为什么微任务非常有用以及它们是如何工作的。如果你并不关心这些内容，你可以跳过这部分或者在你觉得需要的时候再倒回来查看。

当一段 JavaScript 代码在运行的时候，它实际上是运行在**执行上下文**中。下面 3 种类型的代码会创建一个新的执行上下文：

* 全局上下文是为运行代码主体而创建的执行上下文，也就是说，它是为那些存在于 JavaScript 函数之外的任何代码而创建的。
* 每个函数会在执行的时候创建自己的执行上下文。这个上下文就是通常说的“本地上下文”。
* 使用 `eval()` 函数也会创建一个新的执行上下文。

每一个上下文在本质上都是一种作用域层级。每个代码段开始执行的时候都会创建一个新的上下文来运行它，并且在代码退出的时候销毁掉。看看下面这段 JavaScript 程序：

```js
let outputElem = document.getElementById("output");

let userLanguages = {
  Mike: "en",
  Teresa: "es",
};

function greetUser(user) {
  function localGreeting(user) {
    let greeting;
    let language = userLanguages[user];

    switch (language) {
      case "es":
        greeting = `¡Hola, ${user}!`;
        break;
      case "en":
      default:
        greeting = `Hello, ${user}!`;
        break;
    }
    return greeting;
  }
  outputElem.innerHTML += `${localGreeting(user)}<br>\r`;
}

greetUser("Mike");
greetUser("Teresa");
greetUser("Veronica");
```

这段程序代码包含了三个执行上下文，其中有些会在程序运行的过程中多次创建和销毁。每个上下文创建的时候会被推入**执行上下文栈**。当退出的时候，它会从上下文栈中移除。

* 程序开始运行时，全局上下文就会被创建好。
    + 当执行到 `greetUser("Mike")` 的时候，会为 `greetUser()` 函数创建一个它的上下文。这个执行上下文会被推入执行上下文栈中。
        - 当 `greetUser()` 调用 `localGreeting()` 的时候，会为该方法创建一个新的上下文。当 `localGreeting()` 返回的时候，它的上下文也会从执行栈中弹出并销毁。程序会从栈中获取下一个上下文并恢复执行，也就是从 `greetUser()` 剩下的部分开始执行。
        - `greetUser()` 执行完毕并退出，其上下文也从栈中弹出并销毁。
    + 当执行到 `greetUser("Teresa")` 的时候，程序又会为它创建一个上下文并推入栈顶。
        - 当 `greetUser()` 调用 `localGreeting()` 的时候，会为该方法创建一个新的上下文。当 `localGreeting()` 返回的时候，它的上下文也会从执行栈中弹出并销毁。然后，继续执行 `greetUser()` 剩下的部分。
        - `greetUser()` 执行完毕并退出，其上下文也从栈中弹出并销毁。
    + 当执行到 `greetUser("Veronica")` 的时候，程序又会为它创建一个上下文并推入栈顶。
        - 当 `greetUser()` 调用 `localGreeting()` 的时候，会为该方法创建一个新的上下文。当 `localGreeting()` 返回的时候，它的上下文也会从执行栈中弹出并销毁。
        - `greetUser()` 执行完毕并退出，其上下文也从栈中弹出并销毁。
* 主程序退出，全局执行上下文从执行栈中弹出。此时栈中所有的上下文都已经弹出，程序执行完毕。

通过这种方式来使用执行上下文，每个程序和函数都能够拥有自己的变量和其他对象。每个上下文还能够额外的跟踪程序中下一行需要执行的代码以及一些对上下文非常重要的信息。通过这种方式来使用上下文和上下文栈，我们可以对程序运行的一些基础部分进行管理，包括局部和全局变量、函数的调用与返回等。

关于递归函数（即多次调用自身的函数），需要特别注意：每次递归调用自身都会创建一个新的上下文。这使得 JavaScript 运行时能够追踪递归的层级以及从递归中得到的返回值，但这也意味着每次递归都会消耗内存来创建新的上下文。

JavaScript 运行时
--------------

在执行 JavaScript 代码的时候，JavaScript 运行时实际上维护了一组用于执行 JavaScript 代码的**代理**。每个代理由一组执行上下文的集合、执行上下文栈、主线程、一组可能创建用于执行 worker 的额外的线程集合、一个任务队列以及一个微任务队列构成。除了主线程（某些浏览器在多个代理之间共享的主线程）之外，其他组成部分对该代理都是唯一的。

现在我们来更加详细的了解一下运行时是如何工作的。

### 事件循环

每个代理都是由**事件循环**（Event loop）驱动的，事件循环负责收集事件（包括用户事件以及其他非用户事件等）、对任务进行排队以便在合适的时候执行回调。然后它执行所有处于等待中的 JavaScript 任务，然后是微任务，然后在开始下一次循环之前执行一些必要的渲染和绘制操作。

网页或者 app 的代码和浏览器本身的用户界面程序运行在相同的**线程**中，共享相同的**事件循环**。该线程就是**主线程**，它除了运行网页本身的代码之外，还负责收集和派发用户和其他事件，以及渲染和绘制网页内容等。

事件循环驱动着浏览器中发生的一切，因为它与用户的交互有关，但对于我们这里的目的来说，更重要的是它负责调度和执行在其线程中运行的每一段代码。

有如下三种事件循环：

Window 事件循环

window 事件循环驱动所有共享同源的窗口（尽管这有进一步的限制，如下所述）。

Worker 事件循环

worker 事件循环驱动 worker 的事件循环。这包括所有形式的 worker，包括基本的 web worker、shared worker 和 service worker。Worker 被保存在一个或多个与“主”代码分开的代理中；浏览器可以对所有特定类型的工作者使用一个事件循环，也可以使用多个事件循环来处理它们。

Worklet 事件循环

worklet (en-US) 事件循环驱动运行 worklet 的代理。这包含了 `Worklet` (en-US)、`AudioWorklet` (en-US) 以及 `PaintWorklet` (en-US)。

多个同源窗口可能运行在相同的事件循环中，每个队列任务进入到事件循环中以便处理器能够轮流对它们进行处理。记住这里的网络术语“window”实际上指的是“用于运行网页内容的浏览器级容器”，包括实际的 window、标签页或者一个 frame。

在特定情况下，同源窗口之间共享事件循环，例如：

* 如果一个窗口打开了另一个窗口，它们可能会共享一个事件循环。
* 如果窗口是包含在 `<iframe>` 中的容器，则它可能会和包含它的窗口共享一个事件循环。
* 在多进程浏览器中多个窗口碰巧共享了同一个进程。

这种特定情况依赖于浏览器的具体实现，各个浏览器可能并不一样。

#### 任务 vs 微任务

一个**任务**就是指计划由标准机制来执行的任何 JavaScript，如程序的初始化、事件触发的回调等。除了使用事件，你还可以使用 `setTimeout()` 或者 `setInterval()` 来添加任务。

任务队列和微任务队列的区别很简单，但却很重要：

* 当执行来自任务队列中的任务时，在每一次新的事件循环开始迭代的时候运行时都会执行队列中的每个任务。在每次迭代开始之后加入到队列中的任务需要*在下一次迭代开始之后才会被执行*。
* 每次当一个任务退出且执行上下文栈为空的时候，微任务队列中的每一个微任务会依次被执行。不同的是它会等到微任务队列为空才会停止执行——即使中途有微任务加入。换句话说，微任务可以添加新的微任务到队列中，这些新的微任务将在下一个任务开始运行之前，在当前事件循环迭代结束之前执行。
### 问题

由于你的代码和浏览器的用户界面运行在同一个线程中，共享同一个事件循环，假如你的代码阻塞了或者进入了无限循环，则浏览器将会卡死。无论是由于 bug 引起还是代码中进行复杂的运算导致的性能降低，都会降低用户的体验。

当来自多个程序的多个代码对象尝试同时运行的时候，一切都可能变得很慢甚至被阻塞，更不要说浏览器还需要时间来渲染和绘制网站和 UI、处理用户事件等。

### 解决方案

使用 web worker 可以让主线程另起新的线程来运行脚本，这能够缓解上面的情况。一个设计良好的网站或应用会把一些复杂的或者耗时的操作交给 worker 去做，这样可以让主线程除了更新、布局和渲染网页之外，尽可能少的去做其他事情。

通过使用像 promise 这样的异步 JavaScript 技术可以使得主线程在等待请求返回结果的同时继续往下执行，这能够更进一步减轻上面提到的情况。然而，一些更接近于基础功能的代码——比如一些框架代码，可能更需要将代码安排在主线程上一个安全的时间来运行，它与任何请求的结果或者任务无关。

微任务是另一种解决该问题的方案，通过将代码安排在下一次事件循环开始之前运行而不是必须要等到下一次开始之后才执行，这样可以提供一个更好的访问级别。

微任务队列已经存在有一段时间了，但之前它仅仅被内部使用来驱动诸如 promise 这些任务。`queueMicrotask()` 的加入可以让开发者创建一个统一的微任务队列，在任何需要有能力安排代码在 JavaScript 执行上下文栈上没有执行上下文时安全运行的地方使用。在多个实例、浏览器和 JavaScript 运行时中，标准化的微队列机制意味着这些微任务将以相同的顺序可靠地运行，从而避免潜在的难以发现的错误。

参见
--

* 微任务指南
* `queueMicrotask()`
* 异步 JavaScript
    + 异步 JavaScript 简介
    + 合作的异步 JavaScript：超时和间隔
    + 用 Promise 进行优雅的异步编程


Touch.target - Web API 接口参考

Touch.target
============

概述
--

这个属性返回触摸点最初接触的 `Element`，即使这个触摸点已经移出那个元素的交互区域，甚至移出文档。需要注意的是，如果这个元素在触摸过程中被移除，这个事件仍然会指向它，因此这个事件也不会冒泡到 `window` 或 `document` 对象。因此，如果有元素在触摸过程中可能被移除，最佳实践是将触摸事件的监听器绑定到这个元素本身，防止元素被移除后，无法再从它的上一级元素上侦测到从该元素冒泡的事件。

语法
--
```

var el = touchPoint.target;
```

### 返回值

`el`

`Touch` 对象的目标元素。

示例
--

这个例子展示了如何访问 `Touch` 对象的 `Touch.target` 属性。`Touch.target` 属性是最初接触平面的触摸点下的 `Element` (`EventTarget`) 。

在下面的代码片段中，我们假设用户在 `source` 元素上开始接触，因此初始化了一个或多个触摸点。当这个元素上的 `touchstart` 事件处理程序被调用时，每个触摸点的 `Touch.target` 属性可经事件的 `TouchEvent.targetTouches` 列表访问。

```js
// 为'source'元素注册一个触摸监听器
var src = document.getElementById("source");

src.addEventListener(
  "touchstart",
  function (e) {
    // 在这个元素上激活的触点间循环
    for (var i = 0; i < e.targetTouches.length; i++) {
      console.log(
        "touchpoint[" + i + "].target = " + e.targetTouches[i].target,
      );
    }
  },
  false,
);
```

规范
--

| Specification |
| --- |
| Touch Events # dom-touch-target |

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

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


Touch.identifier - Web API 接口参考

Touch.identifier
================

### 概述

返回一个可以唯一地识别和触摸平面接触的点的值。这个值在这根手指（或触摸笔等）所引发的所有事件中保持一致，直到它离开触摸平面。

### 语法
```

var id = touchItem.identifier;
```

### 返回值

`id`

`Touch` 对象的唯一标识符。

### 标准定义

Touch Events Specification


Touch.pageX - Web API 接口参考

Touch.pageX
===========

### 概述

触点相对于 HTML 文档左边沿的的 X 坐标。和 `clientX` 属性不同，这个值是相对于整个 html 文档的坐标，和用户滚动位置无关。因此当存在水平滚动的偏移时，这个值包含了水平滚动的偏移。

### 语法
```

var x = touchItem.pageX;
```

### 返回值

`x`

触点相对于 HTML 文档左边沿的的 X 坐标。当存在水平滚动的偏移时，这个值包含了水平滚动的偏移。

### 标准定义

Touch Events Specification

### 相关链接

* `Touch.pageY`
* `Touch.screenX`
* `Touch.clientX`


Touch() - Web API 接口参考

Touch()
=======

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

**`Touch()`** 构造器创建一个新的 `Touch` 对象。

Syntax
------
```

 touch = new Touch(touchInit);
```

### Arguments

*touchInit*

是一个`TouchInit` 字典，它拥有以下属性：

* `"identifier"`, 必须，是一个长整型，表示一个触摸点的数字标记。
* `"target"`, 必须，是 `EventTarget`类型，表示在触摸点开始接触接触面时的节点。
* `"clientX"`, 可选，默认为 0，为双精度浮点数类型，表示触摸在浏览器视口的横轴坐标，不包括滚动条的偏移距离。
* `"clientY"`, 可选，默认为 0，为双精度浮点数类型，表示触摸在浏览器视口的横轴坐标，不包括滚动条的偏移距离。
* `"screenX"`, 可选，默认为 0，为双精度浮点数类型，表示以用户屏幕为基准的，触摸点横坐标。
* `"screenY"`, 可选，默认为 0，为双精度浮点数类型，表示以用户屏幕为基准的，触摸点纵坐标。
* `"pageX"`,可选，默认为 0，为双精度浮点数类型，表示触摸在用户屏幕的横轴坐标，包括滚动条的偏移距离。
* `"pageY"`, 可选，默认为 0，为双精度浮点数类型，表示触摸在用户屏幕的纵轴坐标，包括滚动条的偏移距离。
* `"radiusX"`, 可选，默认为 0，为浮点数类型。表示接触面（比如手指，触控笔）接触形成的椭圆，在 rotationAngle 角度下横轴上形成的椭圆半径。和 screenX 使用的 CSS 像素保持同一个缩放大小。这个值不能为负。
* `"radiusY"`, 可选，默认为 0，为浮点数类型。表示接触面（比如手指，触控笔）接触形成的椭圆，在 rotationAngle 角度下纵轴上形成的椭圆半径。和 screenY 使用的 CSS 像素保持同一个缩放大小。这个值不能为负。
* `"rotationAngle"`, 可选，默认为 0，为浮点数类型。表示由 radiusX 和 radiusY 决定的椭圆在顺时针方向相对其中心偏转的角度。这个值介于 0 到 90 度之间。如果由 radiusX 和 radiusY 决定的椭圆是一个标准圆形，则 rotationAngle 没有任何效用。用户设备可能用 0 表示这种标准圆形的情况，或者用其他符合要求范围的值来表示（比如，用户设备可能用上一次的触摸事件 rotationAngle 值，来避免突然变动）。
* `"force"`,可选，默认为 0，为浮点数类型。表示触摸体对触摸面的压力值。范围为从 0 到 1:0 表示压力为零，1 表示设备能承受的最大压力敏感值。对压力的敏感值变动范围根据不同环境变动比较大。

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

| Specification |
| --- |
| Touch Events # dom-touch-constructor |

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

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

See also
--------

* `TouchEvent`, the interface of the objects it constructs.


Touch.radiusY - Web API 接口参考

Touch.radiusY
=============

### 概述

能够包围用户和触摸平面的接触面的最小椭圆的垂直轴 (Y 轴) 半径。这个值的单位和 `screenY` 相同。

这个属性和 `Touch.radiusX`、`Touch.rotationAngle` 一起，描述了用户和触摸平面的接触面。这在面向非精确触摸设备 (由手指直接操作的触摸屏) 开发时非常有用。这些值描述了一个尽可能接近实际接触面 (例如用户的指尖) 的椭圆。

### 语法
```

var yRadius = touchItem.radiusY;
```

### 返回值

`yRadius`

能够包围用户和触摸平面的接触面的最小椭圆的垂直轴 (Y 轴) 半径。

### 标准定义

Touch Events Specification

### 相关链接

* `Touch.radiusX`
* `Touch.rotationAngle`
* `Touch.force`


Touch.pageY - Web API 接口参考

Touch.pageY
===========

### 概述

触点相对于 HTML 文档上边沿的的 Y 坐标。和 `clientY` 属性不同，这个值是相对于整个 html 文档的坐标，和用户滚动位置无关。因此当存在垂直滚动的偏移时，这个值包含了垂直滚动的偏移。

### 语法
```

var y = touchItem.pageY;
```

### 返回值

`y`

触点相对于 HTML 文档上边沿的的 Y 坐标。当存在垂直滚动的偏移时，这个值包含了垂直滚动的偏移。

### 标准定义

Touch Events Specification

### 相关链接

* `Touch.pageX`
* `Touch.screenY`
* `Touch.clientY`


Touch.radiusX - Web API 接口参考

Touch.radiusX
=============

### 概述

能够包围用户和触摸平面的接触面的最小椭圆的水平轴 (X 轴) 半径。这个值的单位和 `screenX` 相同。

这个属性和 `Touch.radiusY` 、`Touch.rotationAngle` 一起，描述了用户和触摸平面的接触面。这在面向非精确触摸设备 (由手指直接操作的触摸屏) 开发时非常有用。这些值描述了一个尽可能接近实际接触面 (例如用户的指尖) 的椭圆。

### 语法
```

var xRadius = touchItem.radiusX;
```

### 返回值

`xRadius`

能够包围用户和触摸平面的接触面的最小椭圆的水平轴 (X 轴) 半径。

### 标准定义

Touch Events Specification

### 相关链接

* `Touch.radiusY`
* `Touch.rotationAngle`
* `Touch.force`


Touch.rotationAngle - Web API 接口参考

Touch.rotationAngle
===================

### 概述

返回以度为单位的旋转角。由 `radiusX` 和 `radiusY` 描述的正方向的椭圆，通过顺时针旋转这个角度后，能最精确地覆盖住用户和触摸平面的接触面的角度。这个值可能从 0 到 90。这三个值一起描述了用户和触摸平面的接触面的形状的大小。例如，当用户使用手指和平面接触时，它可能是一个较大的椭圆，而当用户使用触摸笔时，它可能是很小的椭圆。

### 语法
```

var angle = touchItem.rotationAngle;
```

### 返回值

`angle`

以度为单位的旋转角。由 `radiusX` 和 `radiusY` 描述的正方向的椭圆，通过顺时针旋转这个角度后，能最精确地覆盖住用户和触摸平面的接触面的角度。

### 标准定义

Touch Events Specification

### 相关链接

* `Touch.radiusX`
* `Touch.radiusY`
* `Touch.force`


Touch.force - Web API 接口参考

Touch.force
===========

### 概述

返回这个 `Touch` 对象对应的手指挤压触摸平面的压力大小，从 0.0(没有压力) 到 1.0(最大压力) 的浮点数。

### 语法
```

var force = touchItem.force;
```

### 返回值

`force`

手指挤压触摸平面的压力大小。从 0.0(没有压力) 到 1.0(设备可识别的最大压力) 的浮点数。

### 标准定义

Touch Events Specification

### 相关链接

* `Touch.radiusX`
* `Touch.radiusY`
* `Touch.rotationAngle`


Touch.screenX - Web API 接口参考

Touch.screenX
=============

### 概述

返回触点相对于屏幕左边沿的的 X 坐标。不包含页面滚动的偏移量。

### 语法
```

var x = touchItem.screenX;
```

### 返回值

`x`

触点相对于屏幕左边沿的的 X 坐标。不包含页面滚动的偏移量。

### 标准定义

Touch Events Specification

### 相关链接

* `Touch.screenY`
* `Touch.clientX`
* `Touch.pageX`


Touch.clientX - Web API 接口参考

Touch.clientX
=============

### 概述

返回触点相对于可见视区 (visual viewport) 左边沿的的 X 坐标。不包括任何滚动偏移。这个值会根据用户对可见视区的缩放行为而发生变化。

### 语法
```

var x = touchItem.clientX;
```

### 返回值

`x`

触点相对于可见视区 (visual viewport) 左边沿的的 X 坐标。不包括任何滚动偏移。

### 标准定义

Touch Events Specification

### 相关链接

* `Touch.clientY`
* `Touch.screenX`
* `Touch.pageX`


Touch.clientY - Web API 接口参考

Touch.clientY
=============

### 概述

返回触点相对于可见视区 (visual viewport) 上边沿的的 Y 坐标。不包括任何滚动偏移。这个值会根据用户对可见视区的缩放行为而发生变化。

### 语法
```

var y = touchItem.clientY;
```

### 返回值

`y`

返回触点相对于可见视区 (visual viewport) 上边沿的的 Y 坐标。不包括任何滚动偏移。

### 标准定义

Touch Events Specification

### 相关链接

* `Touch.clientX`
* `Touch.screenY`
* `Touch.pageY`


Touch.screenY - Web API 接口参考

Touch.screenY
=============

### 概述

返回触点相对于屏幕上边沿的 Y 坐标。不包含页面滚动的偏移量。

### 语法
```

var y = touchItem.screenY;
```

### 返回值

`y`

触点相对于屏幕上边沿的 Y 坐标。不包含页面滚动的偏移量。

### 标准定义

Touch Events Specification

### 相关链接

* `Touch.screenX`
* `Touch.clientY`
* `Touch.pageY`


MediaCapabilitiesInfo - Web API 接口参考

MediaCapabilitiesInfo
=====================

Media Capabilities API (en-US)的\*\*`MediaCapabilitiesInfo`\*\* 接口在 `MediaCapabilities` (en-US)接口的 `MediaCapabilities.encodingInfo()` 或 `MediaCapabilities.decodingInfo()` (en-US) 方法返回的 promise 完成时变得可用。它提供了媒体类型是否支持，在编码或解码此媒体时是否流畅和能效等信息。

属性
--

`MediaCapabilitiesInfo` 接口包含 3 个布尔属性：

* `supported`: 给出 `MediaConfiguration` 中定义的属性，确定指定的媒体内容能否完全被 编码 (如果 `MediaEncodingConfiguration` (en-US) 已设置) 或 解码 (如果 `MediaDecodingConfiguration` (en-US) 已设置) 。如果是，`supported` 为 *true*. 否则为 *false*.
* `smooth`: 给出 `MediaConfiguration` 中定义的属性，确定指定的媒体内容在回放时能否高质量和流畅。如果 `supported` 为 `true`, 并且回放时将流畅，`smooth` 为 *true*, 否则为 *false.*
* `powerEfficient`: 给出 `MediaConfiguration` 中定义的属性，确定指定的媒体内容在回放时能否高能效。如果 `supported` 为 `true`, 并且回放时将高能效，`powerEfficient` 为 *true*, 否则为 *false.*

浏览器将报告一个支持的媒体配置为 `smooth` 和 `powerEfficient` 直到此设备的统计信息被记录。所有受支持的音频编解码器将报告为高能效。

范例
--

```js
// 测试的 MediaConfiguration
const mediaConfig = {
    type : 'file',
    audio : {
        contentType : "audio/ogg",
        channels : 2,
        bitrate : 132700,
        samplerate : 5200
     },
};

// 检查支持和性能
navigator.mediaCapabilities.decodingInfo(mediaConfig).then(result => { // result 包含媒体兼容信息
    console.log('This configuration is ' +
        (result.supported ? '' : 'not ') + 'supported, ' +             // 配置的媒体能否被用户代理解码？
        (result.smooth ? '' : 'not ') + 'smooth, and ' +               // 是否流畅？
        (result.powerEfficient ? '' : 'not ') + 'power efficient.').   // 是否高能效？
});
```

规范
--

| Specification |
| --- |
| Media Capabilities # ref-for-dom-mediacapabilities-encodinginfo |

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

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

参考
--

* `MediaConfiguration`
* `MediaCapabilities.encodingInfo()`
* `MediaCapabilities.decodingInfo()` (en-US)


FileSystemDirectoryReader.readEntries() - Web API 接口参考

FileSystemDirectoryReader.readEntries()
=======================================

`FileSystemDirectoryReader` (en-US) 接口的 **`readEntries()`** 方法用于检索正在读取的目录中的目录条目，并将它们以数组的形式传递给提供的回调函数。

数组中的对象都是 `FileSystemEntry` (en-US) 实例。它们要么是代表标准文件的 `FileSystemFileEntry` 对象，要么是代表目录的 `FileSystemDirectoryEntry` 对象。

语法
--

```js
readEntries(successCallback)
readEntries(successCallback, errorCallback)
```

### 参数

`successCallback`

在目录中的内容被成功检索时调用的回调函数。该函数接收一个输入参数——文件系统条目对象的数组，其中的每一个对象都基于 `FileSystemEntry` (en-US)。通常，它们要么是代表标准文件的 `FileSystemFileEntry` 对象，要么是代表目录的 `FileSystemDirectoryEntry` 对象。如果目录中没有剩余的文件，或是你已经调用过 `FileSystemDirectoryReader` (en-US) 的 `readEntries()` 方法，则数组为空。

`errorCallback` 可选

一个在目录读取发生错误时调用的回调函数。它接收一个输入参数——一个 `DOMException` 对象，用于描述发生的错误。

### 返回值

无（`undefined`）。

示例
--

参见 `DataTransferItem.webkitGetAsEntry()` 以获取使用此方法的示例代码。

规范
--

| Specification |
| --- |
| File and Directory Entries API # dom-filesystemdirectoryreader-readentries |

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

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

在 Chrome 77 上，`readEntries()` 只会返回前 100 个 `FileSystemEntry` 实例。为了获取所有的实例，请多次调用 `readEntries()`。

参见
--

* 文件与目录条目 API
* 文件与目录条目 API 简介
* `FileSystemDirectoryEntry`
* `FileSystem`


使用文档对象模型 - Web API 接口参考

使用文档对象模型
========

*文档对象模型*（DOM）是一个用于操作 HTML 和 XML 文档（包括其他树状文档）的 DOM 树的 API。这个 API 是页面描述的根源，并作为 web 脚本的基础。

什么是内容树？
-------

**DOM 树**是一种树状结构，其节点代表 HTML 或 XML 文档内容。每一个 HTML 或 XML 文档都有其 DOM 树表示方法。例如，考虑如下文档：

```html
<html lang="en">
  <head>
    <title>My Document</title>
  </head>
  <body>
    <h1>Header</h1>
    <p>Paragraph</p>
  </body>
</html>
```

其 DOM 树类似于这样：

![DOM 是一种树状的文档表示，它有一个根和包含内容的节点元素](/zh-CN/docs/Web/API/Document_object_model/Using_the_Document_Object_Model/using_the_w3c_dom_level_1_core-doctree.jpg)

虽然上面的树与上面文档的 DOM 树相似，但并不完全相同，因为实际的 DOM 树保留了空白。

当 web 浏览器解析 HTML 文档时，它建立了一个 DOM 树，然后用它来显示文档。

文档 API 会做些什么？
-------------

文档 API，有时也称为 DOM API，允许你以任何你想要的方式修改 DOM 树。它使你能够从头开始创建任何 HTML 或 XML 文档，或者改变一个给定的 HTML 或 XML 文档的任何内容。网页作者可以使用 JavaScript 来编辑文档的 DOM，访问全局对象的 `document` 属性。这个 `document` 对象实现了 `Document` 接口。

一个简单的示例
-------

假设作者改变上面文档的头部内容，并且使用两个段落取代一个段落。实现代码如下：

### HTML

```html
<html lang="zh-CN">
  <head>
    <title>我的文档</title>
  </head>
  <body>
    <input type="button" value="改变这个文档。" onclick="change()" />
    <h2>头部</h2>
    <p>段落</p>
  </body>
</html>
```

### JavaScript

```js
function change() {
  // document.getElementsByTagName("h2") 返回文档中所有 <h2> 组成的 NodeList，第一项用数字 0 取到：
  const header = document.getElementsByTagName("h2").item(0);

  // header 的第一个子节点（firstChild）是一个文本节点
  header.firstChild.data = "一个动态文档";

  // 现在 header 的内容为“一个动态文档”

  // 访问第一个段落
  const para = document.getElementsByTagName("p").item(0);
  para.firstChild.data = "这是第一段。";

  // 创建一个用于引用第二段的文本节点
  const newText = document.createTextNode("这是第二段。");

  // 创建一个作为第二段的元素
  const newElement = document.createElement("p");

  // 将文本放入段落元素中
  newElement.appendChild(newText);

  // 通过向 body（para 的父节点）末尾添加的方式将段落元素放在文档的末尾
  para.parentNode.appendChild(newElement);
}
```

想学习更多？
------

现在你已经熟悉了 DOM 的基本概念，你可能想通过阅读如何用 JavaScript 和 DOM 接口遍历 HTML 表格来了解更多关于文档 API 的基本特性。

参见
--

* 文档对象模型（DOM）


使用选择器定位 DOM 元素 - Web API 接口参考

使用选择器定位 DOM 元素
==============

Selectors API 提供了通过与一组选择器匹配来轻松快速地从 DOM 检索 `Element` 节点的方法。这比以前的技术要快得多，例如，过去的技术需要在 JavaScript 代码中使用循环来定位你需要查找的特定项目。

NodeSelector 接口
---------------

此规范向实现 `Document`、`DocumentFragment` 或 `Element` 接口的任何对象添加了两种新方法：

`querySelector()`

返回节点子树内与之相匹配的第一个 `Element` 节点。如果没有匹配的节点，则返回 `null`。

`querySelectorAll()`

返回一个包含节点子树内所有与之相匹配的 `Element` 节点的 `NodeList`，如果没有匹配的节点，则返回一个空的 `NodeList`。

**备注：** 由 `querySelectorAll()` 返回的 `NodeList` 不是动态实时的，这意味着在 DOM 中的更改不会在该列表中反映。这和其他 DOM 查询方法返回动态实时节点列表不一样。

你可以在 `Element.querySelector()` 和 `Element.querySelectorAll()` 方法的文档中寻找到更多示例和细节。

选择器
---

选择器方法接受若干选择器来决定应该返回什么元素或哪些元素。这包括选择器列表，所以你可以在一个查询中分组多个选择器。

为了保护用户的隐私，一些伪类不被支持或表现得不同。例如，`:visited` 将不返回任何匹配，`:link` 则被视为 `:any-link`。

只有元素可以被选择，故不支持伪类。

示例
--

要选择所有包含 `warning` 或 `note` 类的所有段落（`p`）元素，你可以这样做：

```js
const special = document.querySelectorAll("p.warning, p.note");
```

也可以通过 ID 来查询，例如：

```js
const el = document.querySelector("#main, #basic, #exclamation");
```

执行上述代码后，`el` 包含文档中的第一个元素，其 ID 是 `main`、`basic` 或 `exclamation` 之一。

`querySelector()` and `querySelectorAll()` 里可以使用任何 CSS 选择器。

参见
--

* 选择器规范
* CSS 选择器
* `Element.querySelector()`
* `Element.querySelectorAll()`
* `Document.querySelector()`
* `Document.querySelectorAll()`


如何创建一个 DOM 树 - Web API 接口参考

如何创建一个 DOM 树
============

本页描述了如何使用 JavaScript 中的 DOM API 创建 XML 文档。

考虑如下 XML 文档：

```xml
<?xml version="1.0"?>
<people>
  <person first-name="eric" middle-initial="H" last-name="jung">
    <address street="321 south st" city="denver" state="co" country="usa"/>
    <address street="123 main st" city="arlington" state="ma" country="usa"/>
  </person>

  <person first-name="jed" last-name="brown">
    <address street="321 north st" city="atlanta" state="ga" country="usa"/>
    <address street="123 west st" city="seattle" state="wa" country="usa"/>
    <address street="321 south avenue" city="denver" state="co" country="usa"/>
  </person>
</people>
```

你可以通过 DOM API 创建该文档的内存表示：

```js
const doc = document.implementation.createDocument("", "", null);
const peopleElem = doc.createElement("people");

const personElem1 = doc.createElement("person");
personElem1.setAttribute("first-name", "eric");
personElem1.setAttribute("middle-initial", "h");
personElem1.setAttribute("last-name", "jung");

const addressElem1 = doc.createElement("address");
addressElem1.setAttribute("street", "321 south st");
addressElem1.setAttribute("city", "denver");
addressElem1.setAttribute("state", "co");
addressElem1.setAttribute("country", "usa");
personElem1.appendChild(addressElem1);

const addressElem2 = doc.createElement("address");
addressElem2.setAttribute("street", "123 main st");
addressElem2.setAttribute("city", "arlington");
addressElem2.setAttribute("state", "ma");
addressElem2.setAttribute("country", "usa");
personElem1.appendChild(addressElem2);

const personElem2 = doc.createElement("person");
personElem2.setAttribute("first-name", "jed");
personElem2.setAttribute("last-name", "brown");

const addressElem3 = doc.createElement("address");
addressElem3.setAttribute("street", "321 north st");
addressElem3.setAttribute("city", "atlanta");
addressElem3.setAttribute("state", "ga");
addressElem3.setAttribute("country", "usa");
personElem2.appendChild(addressElem3);

const addressElem4 = doc.createElement("address");
addressElem4.setAttribute("street", "123 west st");
addressElem4.setAttribute("city", "seattle");
addressElem4.setAttribute("state", "wa");
addressElem4.setAttribute("country", "usa");
personElem2.appendChild(addressElem4);

const addressElem5 = doc.createElement("address");
addressElem5.setAttribute("street", "321 south avenue");
addressElem5.setAttribute("city", "denver");
addressElem5.setAttribute("state", "co");
addressElem5.setAttribute("country", "usa");
personElem2.appendChild(addressElem5);

peopleElem.appendChild(personElem1);
peopleElem.appendChild(personElem2);
doc.appendChild(peopleElem);
```

DOM 树可以：

* 使用 XPath 表达式查询
* 使用 XMLSerializer 转换为字符串
* 使用 `XMLHttpRequest` 发布到 web 服务器上
* 使用 XSLT 或 XLink (en-US) 转换
参见
--

* XML
* XPath
* 解析和序列化 XML
* XMLHttpRequest


Resource Timing API - Web API 接口参考

Resource Timing API
===================

通过 **`Resource Timing API`** 可以获取和分析应用资源加载的详细网络计时数据，应用程序可以使用时间度量标准来确定加载特定资源所需要的时间，比如 `XMLHttpRequest`、`<SVG>`、图片，或者脚本。

**`Resource Timing API`** 为网络事件（如重定向的开始和结束事件、DNS 查找的开始和结束事件、请求开始、响应开始和结束时间等）生成有高分辨率时间戳（high-resolution timestamp）的资源加载时间线，并提供了资源大小和资源类型。

本文档是 Resource Timing API 的概述。更多详细信息，请查阅每个接口的参考说明，Using Resource Timing 和参见的参考链接。有关资源时序处理模型的图示，请参阅 resource timing phases。

`PerformanceResourceTiming` 接口只统计performance entries 中 `entryType` 为 `resource` 类型的 `PerformanceEntry`。

高分辨率时间戳
-------

一些 **`Resource Timing`** 属性返回高分辨率时间戳，顾名思义，代表了高分辨率的时间点。高分辨率时间戳类型是 `DOMHighResTimeStamp`，用双精度数字（double）表示，它的值是一个离散的时间点或者两个离散时间点之间的时间差。

`DOMHighResTimeStamp` 的单位是毫秒（ms），并且应该可以准确到 5 微秒（µs）。但是，如果浏览器无法以提供精确到 5 微秒的时间值（如软硬件限制），则可以将该值表示为以毫秒为单位的精确到毫秒的时间。

资源载入时间戳
-------

应用程序可以获得用于加载资源的各个阶段的时间戳。处理模型中的第一个属性是 `startTime` (en-US)，它在资源加载过程开始之前立即返回时间。`fetchStart` (en-US) 跟踪和重定向处理（如果适用），并在 DNS 查找之前进行。下个阶段是`connectStart` (en-US) 和 `connectEnd` (en-US) 分别是开始连接到服务器和连接建立完成的时间戳。最后三个按顺序分别是：`requestStart` (en-US)——在浏览器开始向服务器请求资源时；`responseStart` (en-US)——资源请求首包返回时；`responseEnd` (en-US)——资源全部接收完成时。如果资源是通过安全连接加载的 `secureConnectionStart` (en-US) 的值将会在 connectStart 和 connectEnd 之间。

**备注：** 当 CORS 生效时，除非服务器的访问策略允许共享这些值，否则这些值中的许多将返回为零。这需要提供资源的服务器发送 `Timing-Allow-Origin` HTTP 响应标头并且指定 origin[s] 来源才能允许获取这些被限制的时间戳。

在非 web 页面本身的域名下，这些属性在默认都会返回 0 值：`redirectStart`、`redirectEnd`、`domainLookupStart`、`domainLookupEnd`、`connectStart`、`connectEnd`、`secureConnectionStart`、`requestStart` 和 `responseStart`。

The `PerformanceResourceTiming` interface also includes several network timing properties. The `redirectStart` (en-US) and `redirectEnd` (en-US) properties return `timestamps` for redirect start and end times, respectively. Likewise, the The `domainLookupStart` (en-US) and `domainLookupEnd` (en-US) properties return `timestamps` for DNS lookup start and end times, respectively.

*This would be a nice place to have a diagram showing the relationships between these segments of the resource loading time.*

Resource size
-------------

`PerformanceResourceTiming`接口有三个属性用来获取一个资源的数据大小。 `transferSize` (en-US) 属性返回所获得的资源大小，包含响应头加上响应体。

The `encodedBodySize` (en-US) property returns the size (in octets) received from the fetch (HTTP or cache), of the *payload body*, **before** removing any applied content-codings. `decodedBodySize` (en-US) returns the size (in octets) received from the fetch (HTTP or cache) of the *message body*, **after** removing any applied content-codings.

Other properties
----------------

The `nextHopProtocol` (en-US) property returns the *network protocol* used to fetch the resource.

The `initiatorType` (en-US) property returns the *type* of resource that initiated the performance entry such as "`css`" for a CSS resource, "`xmlhttprequest`" for an XMLHttpRequest and "`img`" for an image (such as a JPEG).

If the current context is a `worker`, the `workerStart` (en-US) property can be used to obtain a `DOMHighResTimeStamp` when the worker was started.

Methods
-------

The Resource Timing API includes two methods that extend the `Performance` interface. The `clearResourceTimings()` (en-US) method removes all "`resource`" type performance entries from the browser's *resource* performance entry buffer. The `setResourceTimingBufferSize()` (en-US) method sets the resource performance entry buffer size to the specified number of resource `performance entries`.

The `PerformanceResourceTiming` interface's `toJSON()` (en-US) method returns a JSON serialization of a "`resource`" type `performance entry`.

Implementation status
---------------------

As shown in the `PerformanceResourceTiming` interface's Browser Compatibility table, most of these interfaces are broadly implemented by desktop browsers. However, note that some properties have little to no implementation so see each property's "Browser compatibility" section for more specific interoperability data.

To test your browser's support for these interfaces, run the `perf-api-support` application.

参见
--

* Resource Timing Standard; W3C Editor's Draft
* CanIUse data
* Resource Timing practical tips; Steve Souders; 2014 August 21
* Measuring network performance with Resource Timing API; Ilya Grigorik; 2013 December 11
* A Primer for Web Performance Timing APIs; Xiaoqian Wu; W3C Editor's Draft


自定义时间测量 API - Web API 接口参考

自定义时间测量 API
===========

\*\*`User Timing`\*\*接口允许开发者在浏览器性能时间线中创建针对特定应用的 `时间戳`。有两种自定义时间测量事件类型："`mark`" `事件类型`和"`measure`" `事件类型`。

`mark` 事件可以指定任意的名字并且可以在放在应用的任何位置，`measure` 事件也可以指定为任意的名字，但是需要放在两个 mark 之间，所以它实际上是两个 mark 的中间点。

此文档提供了对 `mark` 和 `measure` `性能事件类型`的概览，包括四个拓展了 `Performance` 接口的 `User Timing` 方法。

性能`标记`
------

性能 **`标记`** 是一个由应用创建并指定名称的`性能条目`。这个标记是浏览器性能时间线上的一个`时间戳`。

### 创建一个性能`标记`

`performance.mark()`方法被用来创建一个性能标记。这个方法需要一个参数，标记的名称（例如`performance.mark("mark-1")`）。

`标记的``性能入口`包含以下属性值：

* `entryType` - 设置为"`mark`".
* `name` - 设置为创建该标记时"`name`"参数传入的值
* `startTime` (en-US) - 设置为`标记`被取消时的`时间戳`。
* `duration` (en-US) - 设置为"`0`"（一个没有时间间隔的标记）
### 检索一个性能`标记`

`Performance`接口有三个方法可以用来检索标记：

`performance.getEntries()`

返回性能时间线上的所有`性能条目`。通过筛选`entryType`属性为"`mark`"的条目来获取所有`标记`条目。

`performance.getEntriesByType(entryType)` (en-US)

返回性能事件线上具有指定`entryType`的`性能条目`，通过将`entryType`设置为"`mark`"来获取所有标记条目。

`performance.getEntriesByName(name, entryType)`

返回性能时间线上具有指定`name`和`entryType`的`性能条目` 。将`entryType`设置为"`mark`"来获得所有的标记条目（通过指定`name`来检索更具体的条目)。

### 移除性能`标记`

想要从性能事件线上移除一个特定标记，调用`performance.clearMarks(name)`，`name`是想要移除的标记的名称。如果这个方法被调用时没有传入任何参数，那么性能时间线上所有标记类型的条目都会被移除。

性能`测量`
------

\*\*`measure`\*\*事件同样由程序指定名称，但是它被放在两个标记之间因此实际上是两个标记间的中间点。

### 创建一个性能`测量`

`测量`通过调用`performance.measure(measureName, startMarkName, endMarkName)`来创建，`measureName`指定了该测量的名称，`startMarkName`和`endMarkName`分别指定了性能时间线上该测量前后的两个标记的名称。

`测量``性能条目`包含以下属性值：

* `entryType` - 设置为"`measure`".
* `name` - 设置为测量被创建时"`name`"参数给定的值
* `startTime` (en-US) - 设置为`measure()`被调用时的 `时间戳`。
* `duration` (en-US) - 设置为该测量间隔`DOMHighResTimeStamp`（即结束标记时间戳减去开始标记时间戳的差值)。
### 检索性能`测量`

`Performance`接口有三个方法可以检索一个测量：

`performance.getEntries()`

返回性能时间线上所有的`性能条目`。通过筛选`entryType`属性为"`measure`"的条目来获取所有`测量`条目。

`performance.getEntriesByType(entryType)` (en-US)

返回性能事件线上指定`entryType`的`性能条目`，通过将`entryType`设置为"`measure`"来获取所有的测量条目。

`performance.getEntriesByName(name, entryType)`

返回性能时间线上具有指定 `name`和`entryType`的`性能项目`，将`entryType`设置为"`measure`"来获取所有测量条目（通过指定`name`参数来检索更具体的条目)。

### 移除性能`测量`

想要从性能时间线上移除一个测量，调用`performance.clearMeasures(name)`，`name` 是要移除的测量的名称。如果该方法被调用时没有传入任何参数，那么性能时间线上所有的测量都会被移除。

互操作性
----

如`Performance`接口的浏览器兼容性表所示，`User Timing` 方法被大多数桌面和移动浏览器（唯一的例外是桌面 Safari 和移动版 Safari，Safari Technology Preview 24 已经支持该方法)。

想要测试你的浏览器是否支持该 API，运行`perf-api-support`程序。

相关内容
----

* User Timing Standard; W3C Editor's Draft
* CanIUse data
* A Primer for Web Performance Timing APIs; Xiaoqian Wu; W3C Editor's Draft


Navigation Timing API - Web API 接口参考

Navigation Timing API
=====================

**Navigation Timing API** 提供了可用于衡量一个网站性能的数据。与用于相同目的的其他基于 JavaScript 的机制不同，该 API 可以提供可以更有用和更准确的端到端延迟数据。

Concepts and usage
------------------

你可以使用 Navigation Timing API 在客户端收集性能数据，并用 `XMLHttpRequest` 或其他技术传送到服务端。同时，该 API 使你可以衡量之前难以获取的数据，如卸载前一个页面的时间，在域名解析上的时间，在执行 `load` 事件处理器上花费的总时间等。

Interfaces
----------

`Performance`
返回一个 Performance 对象。该对象由 High Resolution Time API 定义，Navigation Timing API 在此基础上增加了两个属性：`timing` 和 `navigation`

`PerformanceNavigationTiming`
提供了方法和属性来存取关于游览器文档 navigation 事件的相关数据。如文档实际加载/卸载的时间。

已弃用
 `PerformanceTiming`
曾被用来获取 `timing` 的值，用来衡量页面性能。

已弃用
 `PerformanceNavigation`
曾被用来获取 `navigation` 的值，用来描述加载相关的操作。

以下示例显示了如何测量感知加载时间：
```

function onLoad() {
  var now = new Date().getTime();
  var page_load_time = now - performance.timing.navigationStart;
  console.log("User-perceived page loading time: " + page_load_time);
}
```

还有很多以毫秒为单位呈现的测量事件，你可以通过 `PerformanceTiming` 接口得到它们。按照事件发生的先后顺序，这些事件的列表如下：

* navigationStart
* unloadEventStart
* unloadEventEnd
* redirectStart
* redirectEnd
* fetchStart
* domainLookupStart
* domainLookupEnd
* connectStart
* connectEnd
* secureConnectionStart
* requestStart
* responseStart
* responseEnd
* domLoading
* domInteractive
* domContentLoadedEventStart
* domContentLoadedEventEnd
* domComplete
* loadEventStart
* loadEventEnd

`window.performance.navigation` 对象存储了两个属性，它们表示触发页面加载的原因。这些原因可能是页面重定向、前进后退按钮或者普通的 `URL` 加载。

window.performance.navigation.type:

| Constant | Value | Description |
| --- | --- | --- |
| `TYPE_NAVIGATE` | 0 | 导航开始于点击链接、或者在用户代理中输入 URL、或者表单提交、或者通过除下表中 TYPE\_RELOAD 和 TYPE\_BACK\_FORWARD 的脚本初始化操作。 |
| `TYPE_RELOAD` | 1 | 通过刷新操作或者 location.reload() 方法导航。 |
| `TYPE_BACK_FORWARD` | 2 | 通过历史遍历操作导航。 |
| `TYPE_UNDEFINED` | 255 | 其他非以上类型的导航。 |

`window.performance.navigation.redirectCount` 表示到达最终页面前，重定向的次数（如果有的话）。

Navigation Timing API 可以用于收集客户端性能数据，然后通过 XHR 发送给服务端。还可以计算那些其他方法难以计算的数据，如卸载 (unload) 上一个页面的时间、域名查找时间、window.onload 的总时间等等。

示例
--

计算页面加载所需的总时长：
```

var perfData = window.performance.timing;
var pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
```

计算请求返回时长：
```

var connectTime = perfData.responseEnd - perfData.requestStart;
```


WebGL2RenderingContext.createVertexArray() - Web API 接口参考

WebGL2RenderingContext.createVertexArray()
==========================================

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

WebGL 2 API 中的 \*\*`WebGL2RenderingContext.createVertexArray()`\*\*方法创建并初始化（creates and initializes）一个 `WebGLVertexArrayObject` 的对象 (object) ，它代表一个指向顶点数组数据的顶点数组对象（vertex array object (VAO) ），并为不同的顶点数据集提供名称。

语法
--

```js
createVertexArray()
```

### 参数

没有参数

### 返回值

一个`WebGLVertexArrayObject`对象 代表 一个顶点数组对象 (VAO) ，该对象指向顶点数据。

示例
--

```js
var vao = gl.createVertexArray();
gl.bindVertexArray(vao);

// ...
// calls to bindBuffer or vertexAttribPointer
// which will be "recorded" in the VAO
// ...
```

规范
--

| Specification |
| --- |
| WebGL 2.0 Specification # 3.7.17 |

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

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

参见
--

* `WebGLVertexArrayObject`
* `bindVertexArray`


WebGL2RenderingContext.beginQuery() - Web API 接口参考

WebGL2RenderingContext.beginQuery()
===================================

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

WebGL 2 API 的\*\*`WebGL2RenderingContext.beginQuery()`\*\* 方法启动一个异步查询，`target` 参数表明是哪种类型的查询。

语法
--
```

void gl.beginQuery(target, query);
```

### 参数

target

`GLenum` (en-US) 指定查询个的 target，可能的值有：

* `gl.ANY_SAMPLES_PASSED`: Specifies an occlusion query: these queries detect whether an object is visible (whether the scoped drawing commands pass the depth test and if so, how many samples pass).
* `gl.ANY_SAMPLES_PASSED_CONSERVATIVE`: 和以上一样，但是是一个不精确和更快的版本。
* `gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN`: Number of primitives that are written to transform feedback buffers.

`query`

一个`WebGLQuery` 对象用于查询。

### 返回值

None.

例子
--

```js
var query = gl.createQuery();
gl.beginQuery(gl.ANY\_SAMPLES\_PASSED, query);

// ...
```

规范
--

| Specification |
| --- |
| WebGL 2.0 Specification # 3.7.12 |

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

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

参考
--

* `WebGLQuery`


WebGL2RenderingContext.drawBuffers() - Web API 接口参考

WebGL2RenderingContext.drawBuffers()
====================================

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

WebGL 2 API的 **`WebGL2RenderingContext.drawBuffers()`** 方法定义了将写入零散数据（fragment colors）的绘制缓存 (draw buffer)。绘制缓存设置了上一次绑定帧缓存状态，如果没有帧缓存可用的话，则用绘制缓存。

语法
--
```

void gl.drawBuffers(buffers);
```

### 参数

`buffers`

一个 `GLenum` (en-US)的`Array` 对碎片颜色的说明将被写入缓冲区。可能的值有：

* `gl.NONE`: 碎片着色器的输出没有被写入到任何颜色缓存中。
* `gl.BACK`: 碎片着色器的输出被写入到返回的颜色缓存中。
* `gl.COLOR_ATTACHMENT{0-15}`: 碎片着色器的输出被写入当前帧缓存的第 n 个颜色缓存中。Fragment shader output is written in the nth color attachment of the current framebuffer.

### 返回值

没有。

### 报错信息

* 如果 `buffers` 包含了不止一个合法值， `gl.INVALID_ENUM`错误会被抛出
示例
--

```js
gl.drawBuffers([gl.NONE, gl.COLOR\_ATTACHMENT1]);
```

规范
--

| Specification |
| --- |
| WebGL 2.0 Specification # 3.7.11 |

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

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

详见
--

* `WebGL2RenderingContext.clearBuffer[fiuv]()` (en-US)


WebGL2RenderingContext.uniform[1234][uif][v]() - Web API 接口参考

WebGL2RenderingContext.uniform[1234][uif][v]()
==============================================

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

WebGL API的\*\*`WebGL2RenderingContext.uniform[1234][uif][v]()`\*\* 方法提供了 uniform（es）变量的详细值

**备注：** `ui` *意为无符号整数*, `i` *意为整数，*`f` *意为浮点数*, 并且 `v` *意为矢量。*
 并不是所有的组合都是有效的：`u` 不能是 `f`的组合。详见下方语法表格。用 正则表达式概括语法：`uniform[1234](u?i|f)v?`

语法
--
```

void gl.uniform1ui(location, v0);
void gl.uniform2ui(location, v0, v1);
void gl.uniform3ui(location, v0, v1, v2);
void gl.uniform4ui(location, v0, v1, v2, v3);
void gl.uniform1fv(location, data, optional srcOffset, optional srcLength);
void gl.uniform2fv(location, data, optional srcOffset, optional srcLength);
void gl.uniform3fv(location, data, optional srcOffset, optional srcLength);
void gl.uniform4fv(location, data, optional srcOffset, optional srcLength);
void gl.uniform1iv(location, data, optional srcOffset, optional srcLength);
void gl.uniform2iv(location, data, optional srcOffset, optional srcLength);
void gl.uniform3iv(location, data, optional srcOffset, optional srcLength);
void gl.uniform4iv(location, data, optional srcOffset, optional srcLength);
void gl.uniform1uiv(location, data, optional srcOffset, optional srcLength);
void gl.uniform2uiv(location, data, optional srcOffset, optional srcLength);
void gl.uniform3uiv(location, data, optional srcOffset, optional srcLength);
void gl.uniform4uiv(location, data, optional srcOffset, optional srcLength);
```

### 参数

location

一个 `WebGLUniformLocation` 对象包含了本地 uniform 属性的修改。

`value, v0, v1, v2, v3`

一个新的值被应用到 uniform 变量当中。合理情况：

* `Number` 如果是无符号整数值 (则用 `ui`方法),如果是整数值 (则用 `i`方法), 如果是浮点数 (则用`f`方法).
* `Uint32Array` 用于无符号整数向量（矢量）方法 (则用`uiv`方法).

### 返回值

没有返回值

规范
--

| Specification |
| --- |
| WebGL 2.0 Specification # 3.7.8 |

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

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

详见
--

* `WebGLRenderingContext.uniform()`


WebGL2RenderingContext.bindBufferBase() - Web API 接口参考

WebGL2RenderingContext.bindBufferBase()
=======================================

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

WebGL 2 API 的 **`WebGL2RenderingContext.bindBufferBase()`** 方法将一个 `WebGLBuffer` 绑定到某个点 (`target`) 的特定的 `index`上。

语法
--
```

void gl.bindBufferBase(target, index, buffer);
```

### 参数

`target`

`Glenum` (en-US) 指定绑定操作的目标。可能的值：

* `gl.TRANSFORM_FEEDBACK_BUFFER`
* `gl.UNIFORM_BUFFER`

`index`

`GLuint` (en-US) 指定目标（`target`）的 index。

`buffer`

绑定到目标点（`target`）的 `WebGLBuffer` 。

### 返回值

无。

示例
--

```js
gl.bindBufferBase(gl.TRANSFORM\_FEEDBACK\_BUFFER, 0, buffer);
```

规范
--

| Specification |
| --- |
| WebGL 2.0 Specification # 3.7.16 |

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

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

相关链接
----

* `WebGL2RenderingContext.getIndexedParameter()` (en-US)


WebGL2RenderingContext.beginTransformFeedback() - Web API 接口参考

WebGL2RenderingContext.beginTransformFeedback()
===============================================

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

WebGL 2 API 的 **`WebGL2RenderingContext.beginTransformFeedback()`** 方法开始一个变换回传（Transform Feedback）操作。

语法
--
```

void gl.beginTransformFeedback(primitiveMode);
```

### 参数

`primitiveMode`

A `GLenum` (en-US) specifying the output type of the primitives that will be recorded into the buffer objects that are bound for transform feedback. 可能的值：

* `gl.POINTS`
* `gl.LINES`
* `gl.TRIANGLES`

### 返回值

无。

示例
--

```js
var transformFeedback = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM\_FEEDBACK, transformFeedback);
gl.beginTransformFeedback(gl.TRIANGLES);
gl.drawArrays(gl.TRIANGLES, 0, 3);
```

规范
--

| Specification |
| --- |
| WebGL 2.0 Specification # 3.7.15 |

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

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

相关链接
----

* `WebGLTransformFeedback` (en-US)


WebGL2RenderingContext.texImage3D() - Web API 接口参考

WebGL2RenderingContext.texImage3D()
===================================

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

WebGL API 的 \*\*`WebGLRenderingContext.texImage3D()`\*\*方法指定一个 3d（three-dimensional）纹理贴图。

语法
--
```

void gl.texImage3D(target, level, internalformat, width, height, depth, border, format, type, GLintptr offset);

void gl.texImage3D(target, level, internalformat, width, height, depth, border, format, type, HTMLCanvasElement source);
void gl.texImage3D(target, level, internalformat, width, height, depth, border, format, type, HTMLImageElement source);
void gl.texImage3D(target, level, internalformat, width, height, depth, border, format, type, HTMLVideoElement source);
void gl.texImage3D(target, level, internalformat, width, height, depth, border, format, type, ImageBitmap source);
void gl.texImage3D(target, level, internalformat, width, height, depth, border, format, type, ImageData source);
void gl.texImage3D(target, level, internalformat, width, height, depth, border, format, type, ArrayBufferView? srcData);
void gl.texImage3D(target, level, internalformat, width, height, depth, border, format, type, ArrayBufferView srcData, srcOffset);
```

### 参数

`target`

`GLenum` (en-US)指定绑定纹理图像类型。可能值：

* `gl.TEXTURE_3D`: 一个 3D 贴图
* `gl.TEXTURE_2D_ARRAY`: 一个 2D 数组贴图

`level`

`GLint` (en-US)指定细节等级。level0 是基础图片等级，n 是第 n 个 mipmap 纹理衰减等级。（译者注：原文中衰减应该指像素，并且注意，webgl 的 Mipmapping 技术要求顶层图像的行和列的维数均为 2 的幂）

`internalformat`

`GLint` (en-US)指定贴图的颜色组成，可能值为：

* `gl.ALPHA`: 忽略红色，绿色，蓝色分量值只读取 alpha 信息。
* `gl.RGB`: 忽略 alpha 信息，读取红绿蓝分量
* `gl.RGBA`: 从颜色缓冲（colorBuffer）读取红色，绿色，蓝色和 alpha 分量
* `gl.LUMINANCE`:每个颜色组件都是亮度组件，alpha 值为 1.0.
* `gl.LUMINANCE_ALPHA`:每个组件都是亮度/alpha 组件（component）.
* `gl.R8`
* `gl.R16F`
* `gl.`R32F
* `gl.R8UI`
* `gl.RG8`
* `gl.RG16F`
* `gl.RG32F`
* `gl.RGUI`
* `gl.RGB8`
* `gl.SRGB8`
* `gl.RGB565`
* `gl.R11F_G11F_B10F`
* `gl.RGB9_E5`
* `gl.RGB16F`
* `gl.RGB32F`
* `gl.RGB8UI`
* `gl.RGBA8`
* `gl.SRGB_APLHA8`
* `gl.RGB5_A1`
* `gl.RGBA4444`
* `gl.RGBA16F`
* `gl.RGBA32F`
* `gl.RGBA8UI`

`width`

`GLsizei` (en-US)指定纹理的宽度

`height`

`GLsizei` (en-US) 指定纹理的高度

`depth`

`GLsizei` (en-US) 指定纹理的深度信息

`border`

`GLint` (en-US)指定边框宽度，必须为 0

`format`

`GLenum` (en-US)制定纹素的版本。正确的 `内部格式` 组合被列举在 这个列表。

`type`

A `GLenum` (en-US)指定纹素的数据类型，可能值：

* `gl.UNSIGNED_BYTE`: 每个`gl.RGBA`对应 8 个字节
* `gl.UNSIGNED_SHORT_5_6_5`: 红色占五个字节，绿色占六个字节，蓝色占五个字节
* `gl.UNSIGNED_SHORT_4_4_4_4`: 红色占四个字节，绿色占 四 个字节，蓝色占 四 个字节
* `gl.UNSIGNED_SHORT_5_5_5_1`:红色占五个字节，绿色占五个字节，蓝色占五个字节，alpha 占一个字节
* `gl.BYTE`（这些属性的信息原文中均未提到，但是在 webgl1 中出现过，可以适当参考 webgl1 文献）
* `gl.UNSIGNED_SHORT`
* `gl.SHORT`
* `gl.UNSIGNED_INT`
* `gl.INT`
* `gl.HALF_FLOAT`
* `gl.FLOAT`
* `gl.UNSIGNED_INT_2_10_10_10_REV`
* `gl.UNSIGNED_INT_10F_11F_11F_REV`
* `gl.UNSIGNED_INT_5_9_9_9_REV`
* `gl.UNSIGNED_INT_24_8`
* `gl.FLOAT_32_UNSIGNED_INT_24_8_REV` (pixels must be `null`)

`source`

其中一个对象可以用作纹理对象的源：

* `ArrayBufferView`,
* `ImageBitmap`,
* `ImageData`,
* `HTMLImageElement`,
* `HTMLCanvasElement`,
* `HTMLVideoElement`.

offset

一个针对于`WebGLBuffer`所储存数据的`GLintptr` (en-US)字节的偏移量。用来重新加载已经用`WebGLBuffer`绑定到`PIXEL_UNPACK_BUFFER`的`WebGLTexture` 。

### 返回值

没有

例子
--

```js
gl.texImage3D(
  gl.TEXTURE\_3D,
  0, // level
  gl.RGBA, // internalFormat
  1, // width
  1, // height
  1, // depth
  0, // border
  gl.RGBA, // format
  gl.UNSIGNED\_BYTE, // type
  new Uint8Array([0xff, 0x00, 0x00, 0x00]),
); // data
```

规范
--

| Specification |
| --- |
| WebGL 2.0 Specification # 3.7.6 |

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

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

另见
--

* `WebGLRenderingContext.createTexture()`
* `WebGLRenderingContext.bindTexture()`
* `WebGLRenderingContext.texSubImage2D()` (en-US)
* `WebGLRenderingContext.compressedTexImage2D()` (en-US)
* `WebGLRenderingContext.copyTexImage2D()` (en-US)
* `WebGLRenderingContext.getTexParameter()`


WebGL2RenderingContext.createSampler() - Web API 接口参考

WebGL2RenderingContext.createSampler()
======================================

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

WebGL 2 API 定义的 **`WebGL2RenderingContext.createSampler()`** 方法用于创建并初始化 `WebGLSampler` 对象。

句法
--
```

WebGLSampler gl.createSampler();
```

### 参数

无。

### 返回值

`WebGLSampler` 对象。

例子
--

`gl` 必须是 `WebGL2RenderingContext` 类型。WebGL 1 不支持 `WebGLSampler` 对象。

```js
var sampler = gl.createSampler();
```

规范
--

| Specification |
| --- |
| WebGL 2.0 Specification # 3.7.13 |

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

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

另见
--

* `WebGLSampler`


WebGL2RenderingContext.uniformMatrix[234]x[234]fv() - Web API 接口参考

WebGL2RenderingContext.uniformMatrix[234]x[234]fv()
===================================================

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

WebGL 2 API 的 **`WebGL2RenderingContext.uniformMatrix[234]x[234]fv()`** 方法向 uniform 变量中传入指定的矩阵值。

**备注：** 这个方法不用 `2x2`, `3x3`, 和 `4x4` 版本 . 他们通常用`2`, `3`, 和`4`, 分别表示，详见下方语法。

语法
--
```

void gl.uniformMatrix2fv(location, transpose, data, optional srcOffset, optional srcLength);
void gl.uniformMatrix3x2fv(location, transpose, data, optional srcOffset, optional srcLength);
void gl.uniformMatrix4x2fv(location, transpose, data, optional srcOffset, optional srcLength);
void gl.uniformMatrix2x3fv(location, transpose, data, optional srcOffset, optional srcLength);
void gl.uniformMatrix3fv(location, transpose, data, optional srcOffset, optional srcLength);
void gl.uniformMatrix4x3fv(location, transpose, data, optional srcOffset, optional srcLength);
void gl.uniformMatrix2x4fv(location, transpose, data, optional srcOffset, optional srcLength);
void gl.uniformMatrix3x4fv(location, transpose, data, optional srcOffset, optional srcLength);
void gl.uniformMatrix4fv(location, transpose, data, optional srcOffset, optional srcLength);
```

### 参数

location

一个包含想要修改的 uniform 变量的`WebGLUniformLocation` 对象

transpose

一个决定是否转置矩阵的布尔值（ `GLboolean` (en-US)。在 webgl 中必须为`false`。

data

一个包含方阵中浮点数的类数组对象 (TypeArray) `Float32Array`。

### 返回值

没有。

例子
--

```js
gl.uniformMatrix2x3fv(loc, false, [1, 2, 3, 4, 5, 6]);
```

规范
--

| Specification |
| --- |
| WebGL Specification # 5.14.10 |

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

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

令见
--

* `WebGLRenderingContext.uniformMatrix()`


FileSystemFileHandle：createWritable() 方法 - Web API 接口参考

FileSystemFileHandle：createWritable() 方法
========================================

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

`FileSystemFileHandle` 接口的 **`createWritable()`** 方法用于创建一个 `FileSystemWritableFileStream` 对象，可用于写入文件。此方法返回一个可兑现这些写入流的 `Promise` 对象。

任何通过写入流造成的更改在写入流被关闭前都不会反映到文件句柄所代表的文件上。这通常是将数据写入到一个临时文件来实现的，然后只有在写入文件流被关闭后才会用临时文件替换掉文件句柄所代表的文件。

语法
--

```js
createWritable()
createWritable(options)
```

### 参数

`options` 可选

一个包含以下属性的对象：

`keepExistingData`

布尔值，默认为 `false`。当设为 `true` 时，如果文件存在，则现将现有文件的内容复制到临时文件，否则临时文件初始时内容为空。

### 返回值

一个 `Promise` 对象，可兑现一个 `FileSystemWritableFileStream` 对象。

### 异常

NotAllowedError

如果句柄 `readwrite` 模式的 `PermissionStatus.state` (en-US) 不为 `'granted'`，则抛出此异常。

示例
--

以下异步函数用于将给定内容写入文件句柄，从而写入磁盘。

```js
async function writeFile(fileHandle, contents) {
  // 创建一个 FileSystemWritableFileStream 用来写入。
  const writable = await fileHandle.createWritable();

  // 将文件内容写入到流中。
  await writable.write(contents);

  // 关闭文件并将内容写入磁盘。
  await writable.close();
}
```

规范
--

| Specification |
| --- |
| File System Standard # api-filesystemfilehandle-createwritable |

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

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

参见
--

* 文件系统 API
* 文件系统访问 API：简化本地文件访问


FileSystemFileHandle：getFile() 方法 - Web API 接口参考

FileSystemFileHandle：getFile() 方法
=================================

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

`FileSystemFileHandle` 接口的 **`getFile()`** 方法返回一个 `Promise` 对象，可兑现一个 `File` 对象，其表示磁盘上句柄所代表的条目的状态。

如果磁盘上的文件在调用了此方法后发生了更改或是被移除，那么返回的 `File` 对象可能会不再可读。

语法
--

```js
getFile()
```

### 参数

无。

### 返回值

一个 `Promise` 对象，可兑现一个 `File` 对象。

### 异常

NotAllowedError

如果只读模式的 `PermissionStatus.state` (en-US) 不是 `granted` ，则抛出此异常。

示例
--

下面的异步函数可以显示一个文件选择器，并在选择了文件时使用 `getFile()` 方法取得内容。

```js
async function getTheFile() {
  // 打开文件选择器
  const [fileHandle] = await window.showOpenFilePicker(pickerOpts);

  // 获取文件内容
  const fileData = await fileHandle.getFile();
}
```

规范
--

| Specification |
| --- |
| File System Standard # api-filesystemfilehandle-getfile |

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

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

参见
--

* 文件系统 API
* 文件系统访问 API：简化本地文件访问


FileSystemFileHandle：createSyncAccessHandle() 方法 - Web API 接口参考

FileSystemFileHandle：createSyncAccessHandle() 方法
================================================

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

`FileSystemFileHandle` 接口的 **`createSyncAccessHandle()`** 方法返回一个 `Promise` 对象，可兑现一个用于同步读写文件的 `FileSystemSyncAccessHandle` 对象。此方法的同步特性带来了性能优势，但是只能在专用于操作源私有文件系统上的文件的 Web Worker 中使用。

创建 `FileSystemSyncAccessHandle` 会对与文件句柄关联的文件进行独占锁定。这用于在文件已有的访问句柄被关闭前，阻止对文件创建更多的 `FileSystemSyncAccessHandle` 或 `FileSystemWritableFileStream`。

语法
--

```js
createSyncAccessHandle()
```

### 参数

无。

### 返回值

一个 `Promise` 对象，可兑现一个 `FileSystemSyncAccessHandle` 对象。

### 异常

`InvalidStateError` `DOMException`

如果 `FileSystemSyncAccessHandle` 对象代表的不是源私有文件系统上的文件，抛出此异常。

`NoModificationAllowedError` `DOMException`

如果浏览器无法获得文件句柄所关联的文件的锁定，抛出此异常。

`NotAllowedError` `DOMException`

如果在 API 层面上没有被授予权限（也就是需要调用 `FileSystemHandle.requestPermission`），抛出此异常。

示例
--

以下异步事件处理函数处于 Web Worker 上下文。其中的代码片段创建了一个同步文件访问句柄。

```js
onmessage = async (e) => {
  // 获取从主线程发往 worker 的消息
  const message = e.data;

  // 获取草稿文件的句柄
  const root = await navigator.storage.getDirectory();
  const draftHandle = await root.getFileHandle("draft.txt", { create: true });
  // 获取同步访问句柄
  const accessHandle = await draftHandle.createSyncAccessHandle();

  // ……

  // 用完 FileSystemSyncAccessHandle 记得把它关闭
  accessHandle.close();
};
```

规范
--

| Specification |
| --- |
| File System Standard # api-filesystemfilehandle-createsyncaccesshandle |

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

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

参见
--

* 文件系统 API
* 文件系统访问 API：简化本地文件访问


File drag and drop - Web API 接口参考

File drag and drop
==================

HTML 拖放接口使得 web 应用能够在网页中拖放文件。这篇文档介绍了 web 应用如何接受从底层平台的文件管理器拖动一个或多个文件到网页的操作。

拖放的主要步骤是为 `drop` 事件定义一个*释放区*(释放文件的目标元素) 和为`dragover`事件定义一个事件处理程序。下面描述了这些步骤，包括示例程序片段。完整的源码在MDN's drag-and-drop repository (欢迎提交 pull requests 和/或 issues).

注意：`HTML drag and drop`定义了两套不同的 API 来支持拖放文件。一个`DataTransfer`接口和另一个`DataTransferItem`与`DataTransferItemList`接口。这个示例介绍了这两种 API 的用法 (没有使用任何 Gecko 专用的接口)。

定义拖放区域
------

触发 `drop` 事件的目标元素需要一个`ondrop` 事件处理函数。下面这一段代码以一个 `<div>` 元素为例展示了这些工作是如何完成的：

```html
<div id="drop\_zone" ondrop="dropHandler(event);">
  <p>Drag one or more files to this Drop Zone ...</p>
</div>
```

一般来说，在实际应用中需要定义一个 `dragover` 事件的处理函数并在其中加入关闭浏览器默认拖放行为的代码。你需要定义一个 `ondragover` (en-US) 事件处理函数：

```html
<div
 id="drop\_zone"
 ondrop="dropHandler(event);"
 ondragover="dragOverHandler(event);">
  <p>Drag one or more files to this Drop Zone ...</p>
</div>
```

最后，在实际应用中可能需要给实现释放文件的元素加些样式来从视觉上向使用者说明这是一个有效释放区域。在这个示例中，实现释放文件的元素将使用如下样式：

```css
#drop\_zone {
  border: 5px solid blue;
  width: 200px;
  height: 100px;
}
```

**备注：** 注意当执行将文件拖入浏览器的操作时操作系统并不会触发 `dragstart` 和 `dragend` 事件。

执行释放事件
------

当用户释放文件时 `drop` 事件将会被触发。在下面的 drop 处理函数中，如果浏览器支持 `DataTransferItemList` 接口，则可以使用 `getAsFile()` 来获取每个文件；否则使用 `DataTransfer` 接口的 `files` 属性来获取每个文件。

这个示例展示了如何讲每个被拖动的文件的名字输出到控制台。在实际应用中可能会用到 `File API` 来处理一个文件。

注意在这个例子中，任何被拖动的非文件内容都会被忽略。

```js
function dropHandler(ev) {
  console.log("File(s) dropped");

  // Prevent default behavior (Prevent file from being opened)
  ev.preventDefault();

  if (ev.dataTransfer.items) {
    // Use DataTransferItemList interface to access the file(s)
    for (var i = 0; i < ev.dataTransfer.items.length; i++) {
      // If dropped items aren't files, reject them
      if (ev.dataTransfer.items[i].kind === "file") {
        var file = ev.dataTransfer.items[i].getAsFile();
        console.log("... file[" + i + "].name = " + file.name);
      }
    }
  } else {
    // Use DataTransfer interface to access the file(s)
    for (var i = 0; i < ev.dataTransfer.files.length; i++) {
      console.log(
        "... file[" + i + "].name = " + ev.dataTransfer.files[i].name,
      );
    }
  }
}
```

阻止浏览器的默认释放行为
------------

下面的 `dragover` 事件处理函数调用 `preventDefault()` 来关闭浏览器的默认拖动和释放行为。

```js
function dragOverHandler(ev) {
  console.log("File(s) in drop zone");

  // Prevent default behavior (Prevent file from being opened)
  ev.preventDefault();
}
```

参见
--

* HTML Drag and Drop API
* Drag Operations (en-US)
* HTML5 Living Standard: Drag and Drop