diff --git a/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md b/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md
index 164eb68719..e69a363d3c 100644
--- a/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md
+++ b/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md
@@ -1,4 +1,4 @@
-# 拼接类型化数组(Concatenate typed arrays)
+# 拼接类型化数组
给定一个 `Uint8Array` 数组,请写一个函数 `concat(arrays)`,将数组拼接成一个单一数组并返回。
diff --git a/4-binary/01-arraybuffer-binary-arrays/article.md b/4-binary/01-arraybuffer-binary-arrays/article.md
index cbb44427b3..b7b60bef5b 100644
--- a/4-binary/01-arraybuffer-binary-arrays/article.md
+++ b/4-binary/01-arraybuffer-binary-arrays/article.md
@@ -1,84 +1,87 @@
# ArrayBuffer,二进制数组
-在 web 开发中,我们通常会在处理文件(创建、上传、下载)时遇到二进制数据。另一个典型的应用场景是图像处理。
+在 Web 开发中,当我们处理文件时(创建,上传,下载),经常会遇到二进制数据。另一个典型的应用场景是图像处理。
-JavaScript 中同样也会遇到,而且二进制操作性能也高。
+这些都可以通过 JavaScript 进行处理,而且二进制操作性能更高。
-不过,由于 JavaScript 中有很多类,会有点容易混淆。仅举几例:
-- `ArrayBuffer`、`Uint8Array`、`DataView`、`Blob` 和 `File` 等。
+不过,在 JavaScript 中有很多种二进制数据格式,会有点容易混淆。仅举几个例子:
+- `ArrayBuffer`,`Uint8Array`,`DataView`,`Blob`,`File` 及其他。
-与其他语言相比,JavaScript 中二进制的实现方式不是很标准。但当我们理清楚以后,一切就变得相当简单了。
+与其他语言相比,JavaScript 中的二进制数据是以非标准方式实现的。但是,当我们理清楚以后,一切就会变得相当简单了。
-**基本的二进制对象是 `ArrayBuffer` — 对固定长度的连续内存空间的引用。**
+**基本的二进制对象是 `ArrayBuffer` —— 对固定长度的连续内存空间的引用。**
-我们如下创建一个 ArrayBuffer:
+我们这样创建它:
```js run
-let buffer = new ArrayBuffer(16); // 创建一个长度为 16 的缓存区
+let buffer = new ArrayBuffer(16); // 创建一个长度为 16 的 buffer
alert(buffer.byteLength); // 16
```
-它会分配一个 16 字节的连续内存区域,并预先用 0 填充。
+它会分配一个 16 字节的连续内存空间,并用 0 进行预填充。
-```warn header="`ArrayBuffer` 不是某种数组"
-让我们来澄清一个可能的误区。`ArrayBuffer` 与 `Array` 没有任何共同之处:
-- 它长度固定,无法增加或减少。
-- 它正好占用了内存中那么多的空间。
-- 如要访问单个字节,需要另一个“视图”对象,而不是 `buffer[index]`。
+```warn header="`ArrayBuffer` 不是某种东西的数组"
+让我们先澄清一个可能的误区。`ArrayBuffer` 与 `Array` 没有任何共同之处:
+- 它的长度是固定的,我们无法增加或减少它的长度。
+- 它正好占用了内存中的那么多空间。
+- 要访问单个字节,需要另一个“视图”对象,而不是 `buffer[index]`。
```
`ArrayBuffer` 是一个内存区域。它里面存储了什么?无从判断。只是一个原始的字节序列。
**如要操作 `ArrayBuffer`,我们需要使用“视图”对象。**
-视图对象本身并不存储任何元素。它是一副“眼镜”,透过它来解析存储在 `ArrayBuffer` 中的字节。
+视图对象本身并不存储任何东西。它是一副“眼镜”,透过它来解释存储在 `ArrayBuffer` 中的字节。
例如:
-- **`Uint8Array`** — 将 "ArrayBuffer" 中的每个字节视为 0 到 255 之间的单个数字(每个字节是 8 位,因此只能容纳那么多)。此类值称为“8 位无符号整数”。
-- **`Uint16Array`** — 将每 2 个字节视为一个 0 到 65535 的整数。此类值称为“16 位无符号整数”。
-- **`Uint32Array`** — 将每 4 个字节视为一个 0 到 4294967295 之间的整数。此类值称为“32 位无符号整数”。
-- **`Float64Array`** — 将每 8 个字节视为一个 5.0x10-324 到 1.8x10308 之间的浮点数。
+- **`Uint8Array`** —— 将 `ArrayBuffer` 中的每个字节视为 0 到 255 之间的单个数字(每个字节是 8 位,因此只能容纳那么多)。这称为 “8 位无符号整数”。
+- **`Uint16Array`** —— 将每 2 个字节视为一个 0 到 65535 之间的整数。这称为 “16 位无符号整数”。
+- **`Uint32Array`** —— 将每 4 个字节视为一个 0 到 4294967295 之间的整数。这称为 “32 位无符号整数”。
+- **`Float64Array`** —— 将每 8 个字节视为一个 5.0x10-324 到 1.8x10308 之间的浮点数。
-因此,一个 16 字节 `ArrayBuffer` 中的二进制数据可以表示为 16 个“小数字”,或 8 个较大的数字(每个数字 2 个字节),或 4 个更大的数字(每个数字 4 个字节),或 2 个高精度的浮点数(每个数字 8 个字节)。
+因此,一个 16 字节 `ArrayBuffer` 中的二进制数据可以解释为 16 个“小数字”,或 8 个更大的数字(每个数字 2 个字节),或 4 个更大的数字(每个数字 4 个字节),或 2 个高精度的浮点数(每个数字 8 个字节)。

-`ArrayBuffer` 是核心对象,是所有对象的基础,是原始二进制数据。
+`ArrayBuffer` 是核心对象,是所有的基础,是原始的二进制数据。
-但是,如果我们要写入值,或遍历之,基本上几乎任何操作 - 我们必须使用视图(view),例如:
+但是,如果我们要写入值或遍历它,基本上几乎所有操作 —— 我们必须使用视图(view),例如:
```js run
-let buffer = new ArrayBuffer(16); // 创建长度为 16 的缓存区
+let buffer = new ArrayBuffer(16); // 创建一个长度为 16 的 buffer
*!*
-let view = new Uint32Array(buffer); // 将缓存区视为 32 位整数序列
+let view = new Uint32Array(buffer); // 将 buffer 视为一个 32 位整数的序列
alert(Uint32Array.BYTES_PER_ELEMENT); // 每个整数 4 个字节
*/!*
-alert(view.length); // 4,储存了 4 个整数
-alert(view.byteLength); // 16,大小为 16,以字节为单位
+alert(view.length); // 4,它存储了 4 个整数
+alert(view.byteLength); // 16,字节中的大小
// 让我们写入一个值
view[0] = 123456;
// 遍历值
for(let num of view) {
- alert(num); // 123456,然后是 0,0,0(一共 4 个值)
+ alert(num); // 123456,然后 0,0,0(一共 4 个值)
}
```
-## 类型化数组(TypedArray)
+## TypedArray
-所有这些视图(`Uint8Array`、`Uint32Array` 等)有一个通用术语是 [TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects)。它们都享有同一组方法和属性。
+所有这些视图(`Uint8Array`,`Uint32Array` 等)的通用术语是 [TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects)。它们都享有同一组方法和属性。
-它们更像普通数组:有索引,可遍历。
+请注意,没有名为 `TypedArray` 的构造器,它只是表示 `ArrayBuffer` 上的视图之一的通用总称术语:`Int8Array`,`Uint8Array` 及其他,很快就会有完整列表。
+当你看到 `new TypedArray` 之类的内容时,它表示 `new Int8Array`、`new Uint8Array` 及其他中之一。
-类型化数组的构造函数(无论是 `Int8Array` 或 `Float64Array`)各不相同,具体取决于参数类型。
+类型化数组的行为类似于常规数组:具有索引,并且是可迭代的。
-有 5 种参数变量:
+一个类型化数组的构造器(无论是 `Int8Array` 或 `Float64Array`,都无关紧要),其行为各不相同,并且取决于参数类型。
+
+参数有 5 种变体:
```js
new TypedArray(buffer, [byteOffset], [length]);
@@ -88,18 +91,18 @@ new TypedArray(length);
new TypedArray();
```
-1. 如果给定的是 `ArrayBuffer` 参数,则在其上创建视图。我们已经用过该语法了。
+1. 如果给定的是 `ArrayBuffer` 参数,则会在其上创建视图。我们已经用过该语法了。
- 根据需要,我们可以给定起始位置 `byteOffset`(默认为 0)以及 `length`(默认至缓存区的末尾),这样视图就会只涵盖 `buffer` 的一部分。
+ 可选,我们可以给定起始位置 `byteOffset`(默认为 0)以及 `length`(默认至 buffer 的末尾),这样视图将仅涵盖 `buffer` 的一部分。
-2. 如果给定的是 `Array`、或任何类似数组的对象,则创建一个相同长度的类型化数组,并复制值。
+2. 如果给定的是 `Array`,或任何类数组对象,则会创建一个相同长度的类型化数组,并复制其内容。
- 我们可以使用它来预填充数据:
+ 我们可以使用它来预填充数组的数据:
```js run
*!*
let arr = new Uint8Array([0, 1, 2, 3]);
*/!*
- alert( arr.length ); // 4,创建相同长度的二进制数组
+ alert( arr.length ); // 4,创建了相同长度的二进制数组
alert( arr[1] ); // 1,用给定值填充了 4 个字节(无符号 8 位整数)
```
3. 如果给定的是另一个 `TypedArray`,也是如此:创建一个相同长度的类型化数组,并复制其内容。如果需要的话,数据在此过程中会被转换为新的类型。
@@ -112,20 +115,20 @@ new TypedArray();
alert( arr8[1] ); // 232,试图复制 1000,但无法将 1000 放进 8 位字节中(详述见下文)。
```
-4. 对于整型参数 `length` — 创建包含 `length` 这么多元素的类型化数组。它的字节长度将是 `length` 乘以单个 `TypedArray.BYTES_PER_ELEMENT` 中的字节数:
+4. 对于数字参数 `length` —— 创建类型化数组以包含这么多元素。它的字节长度将是 `length` 乘以单个 `TypedArray.BYTES_PER_ELEMENT` 中的字节数:
```js run
let arr = new Uint16Array(4); // 为 4 个整数创建类型化数组
alert( Uint16Array.BYTES_PER_ELEMENT ); // 每个整数 2 个字节
- alert( arr.byteLength ); // 8(大小,以字节为单位)
+ alert( arr.byteLength ); // 8(字节中的大小)
```
-5. 不带参数的情况下,创建零长度的类型化数组。
+5. 不带参数的情况下,创建长度为零的类型化数组。
-我们可以直接创建一个 `TypedArray`,而无需提及 `ArrayBuffer`。但是,视图离不开底层的 `ArrayBuffer`,因此除第一种情况(已提供 `ArrayBuffer`)外,其他所有情况都会自动创建 `ArrayBuffer`。
+我们可以直接创建一个 `TypedArray`,而无需提及 `ArrayBuffer`。但是,视图离不开底层的 `ArrayBuffer`,因此,除第一种情况(已提供 `ArrayBuffer`)外,其他所有情况都会自动创建 `ArrayBuffer`。
如要访问 `ArrayBuffer`,可以用以下属性:
-- `arr.buffer` — 引用 `ArrayBuffer`。
-- `arr.byteLength` — `ArrayBuffer` 的长度。
+- `arr.buffer` —— 引用 `ArrayBuffer`。
+- `arr.byteLength` —— `ArrayBuffer` 的长度。
因此,我们总是可以从一个视图转到另一个视图:
```js
@@ -138,24 +141,24 @@ let arr16 = new Uint16Array(arr8.buffer);
下面是类型化数组的列表:
-- `Uint8Array`,`Uint16Array`,`Uint32Array` — 用于 8、16 和 32 位的整数。
- - `Uint8ClampedArray` — 对于 8 位整数,在赋值时便“固定“其值(见下文)。
-- `Int8Array`,`Int16Array`,`Int32Array` — 用于有符号整数(可以为负数)。
-- `Float32Array`,`Float64Array` — 用于 32 位和 64 位的有符号浮点数。
+- `Uint8Array`,`Uint16Array`,`Uint32Array` —— 用于 8、16 和 32 位的整数。
+ - `Uint8ClampedArray` —— 用于 8 位整数,在赋值时便“固定“其值(见下文)。
+- `Int8Array`,`Int16Array`,`Int32Array` —— 用于有符号整数(可以为负数)。
+- `Float32Array`,`Float64Array` —— 用于 32 位和 64 位的有符号浮点数。
-```warn header="无 `int8` 或类似的单值类型"
-请注意,尽管有类似 `Int8Array` 这样的名称,JavaScript 中并没有像 `int`,或 `int8` 这样的单值类型。
+```warn header="没有 `int8` 或类似的单值类型"
+请注意,尽管有类似 `Int8Array` 这样的名称,但 JavaScript 中并没有像 `int`,或 `int8` 这样的单值类型。
-这是合乎逻辑的,因为 `Int8Array` 不是这些单值的数组,而是 `ArrayBuffer`上的视图。
+这是合乎逻辑的,因为 `Int8Array` 不是这些单值的数组,而是 `ArrayBuffer` 上的视图。
```
### 越界行为
-如果我们尝试将越界值写入类型化数组会出现什么情况?不会报错。但是多余的位被截断。
+如果我们尝试将越界值写入类型化数组会出现什么情况?不会报错。但是多余的位被切除。
-例如,我们试着将 256 放入 `Uint8Array`。256 的二进制格式是 `100000000`(9 位),但 `Uint8Array` 每个值只有 8 位,因此可用范围为 0 到 255。
+例如,我们尝试将 256 放入 `Uint8Array`。256 的二进制格式是 `100000000`(9 位),但 `Uint8Array` 每个值只有 8 位,因此可用范围为 0 到 255。
-对于更大的数字,仅存储最右边的(低位有效)8 位,其余部分被截断:
+对于更大的数字,仅存储最右边的(低位有效)8 位,其余部分被切除:

@@ -182,34 +185,34 @@ alert(uint8array[0]); // 0
alert(uint8array[1]); // 1
```
-`Uint8ClampedArray` 在这方面比较特殊,不太一样。对于大于 255 的任何数字,它将保存为 255;对于任何负数,它将保存为 0。这对于图像处理很有用。
+`Uint8ClampedArray` 在这方面比较特殊,它的表现不太一样。对于大于 255 的任何数字,它将保存为 255,对于任何负数,它将保存为 0。此行为对于图像处理很有用。
## TypedArray 方法
-`TypedArray` 有普通的 `Array` 方法,但有个明显的例外。
+`TypedArray` 具有常规的 `Array` 方法,但有个明显的例外。
-我们可以遍历(iterate)、`map`、`slice`、`find` 和 `reduce`等等。
+我们可以遍历(iterate),`map`,`slice`,`find` 和 `reduce` 等。
-但有几件事我们不能做:
+但有几件事我们做不了:
-- 无 `splice` — 我们不能“删除”一个值,因为类型化数组是缓存区上的视图,并且是固定的、连续的内存区域。我们所能做的就是分配一个零值。
+- 没有 `splice` —— 我们无法“删除”一个值,因为类型化数组是缓存区(buffer)上的视图,并且缓存区(buffer)是固定的、连续的内存区域。我们所能做的就是分配一个零值。
- 无 `concat` 方法。
还有两种其他方法:
- `arr.set(fromArr, [offset])` 将 `fromArr` 中从 `offset`(默认为 0)开始的所有元素复制到 `arr`。
-- `arr.subarray([begin, end])` 创建一个从 `begin` 到 `end`(不包括)相同类型的新视图。这类似于 `slice` 方法(同样也支持),但是不复制任何内容 - 只是创建一个新视图,对给定的数据进行操作。
+- `arr.subarray([begin, end])` 创建一个从 `begin` 到 `end`(不包括)相同类型的新视图。这类似于 `slice` 方法(同样也支持),但不复制任何内容 —— 只是创建一个新视图,以对给定片段的数据进行操作。
-有了这些方法,我们可以复制、混合类型化数组,从现有数组创建新数组,等等。
+有了这些方法,我们可以复制、混合类型化数组,从现有数组创建新数组,等。
-## 数据视图(DataView)
+## DataView
-[DataView](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/DataView) 在 `ArrayBuffer` 上层,是一种特殊的超灵活“无类型”视图。它允许以任何格式访问任何偏移量的数据。
+[DataView](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/DataView) 是在 `ArrayBuffer` 上的一种特殊的超灵活“未类型化”视图。它允许以任何格式访问任何偏移量(offset)的数据。
-- 对于类型数组,构造器决定了其格式。整个数组应该是统一的。第 i 个数字是 `arr[i]`。
-- 通过 `DataView`,我们可以使用 `.getUint8(i)` 或 `.getUint16(i)` 之类的方法访问数据。我们在调用方法的时候选择格式,而不是在构造的时候。
+- 对于类型化的数组,构造器决定了其格式。整个数组应该是统一的。第 i 个数字是 `arr[i]`。
+- 通过 `DataView`,我们可以使用 `.getUint8(i)` 或 `.getUint16(i)` 之类的方法访问数据。我们在调用方法时选择格式,而不是在构造的时候。
语法:
@@ -217,11 +220,11 @@ alert(uint8array[1]); // 1
new DataView(buffer, [byteOffset], [byteLength])
```
-- **`buffer`** — 底层的 `ArrayBuffer`。与类型化数组不同,`DataView` 不会自行创建缓存区。我们需要事先准备好。
-- **`byteOffset`** — 视图的起始字节位置(默认为 0)。
-- **`byteLength`** — 视图的字节长度(默认至 `buffer` 的末尾)。
+- **`buffer`** —— 底层的 `ArrayBuffer`。与类型化数组不同,`DataView` 不会自行创建缓存区(buffer)。我们需要事先准备好。
+- **`byteOffset`** —— 视图的起始字节位置(默认为 0)。
+- **`byteLength`** —— 视图的字节长度(默认至 `buffer` 的末尾)。
-例如,这里我们从同一缓存区中提取不同格式的数字:
+例如,这里我们从同一个 buffer 中提取不同格式的数字:
```js run
// 4 个字节的二进制数组,每个都是最大值 255
@@ -241,7 +244,7 @@ alert( dataView.getUint32(0) ); // 4294967295(最大的 32 位无符号整数
dataView.setUint32(0, 0); // 将 4 个字节的数字设为 0,即将所有字节都设为 0
```
-当我们在同一缓存区内存储混合格式的数据时,`DataView` 非常有用。例如,我们存储一个成对序列(16 位整数,32 位浮点数)。用 `DataView` 来访问便很容易。
+当我们将混合格式的数据存储在同一缓存区(buffer)中时,`DataView` 非常有用。例如,我们存储一个成对序列(16 位整数,32 位浮点数)。用 `DataView` 可以轻松访问它们。
## 总结
@@ -250,19 +253,19 @@ dataView.setUint32(0, 0); // 将 4 个字节的数字设为 0,即将所有字
几乎任何对 `ArrayBuffer` 的操作,都需要一个视图。
- 它可以是 `TypedArray`:
- - `Uint8Array`,`Uint16Array`,`Uint32Array` — 用于 8 位、16 位和 32 位无符号整数。
- - `Uint8ClampedArray` — 用于 8 位整数,在赋值时便“固定”其值。
- - `Int8Array`,`Int16Array`,`Int32Array` — 用于有符号整数(可以为负数)。
- - `Float32Array`,`Float64Array` — 用于 32 位和 64 位的有符号浮点数。
-- 或 `DataView` — 通过方法(methods)来指定格式的视图,例如,`getUint8(offset)`。
+ - `Uint8Array`,`Uint16Array`,`Uint32Array` —— 用于 8 位、16 位和 32 位无符号整数。
+ - `Uint8ClampedArray` —— 用于 8 位整数,在赋值时便“固定”其值。
+ - `Int8Array`,`Int16Array`,`Int32Array` —— 用于有符号整数(可以为负数)。
+ - `Float32Array`,`Float64Array` —— 用于 32 位和 64 位的有符号浮点数。
+- 或 `DataView` —— 使用方法来指定格式的视图,例如,`getUint8(offset)`。
-在多数情况下,我们直接对类型化数组进行创建和操作,而将 “ArrayBuffer” 作为“普通区分器”隐藏起来。我们可以通过 `.buffer` 来访问它,并在需要时创建另一个视图。
+在大多数情况下,我们直接对类型化数组进行创建和操作,而将 `ArrayBuffer` 作为“通用标识符(common discriminator)”隐藏起来。我们可以通过 `.buffer` 来访问它,并在需要时创建另一个视图。
-还有另外两个术语,它们用在二进制数据操作的方法描述中:
+还有另外两个术语,用于对二进制数据进行操作的方法的描述:
- `ArrayBufferView` 是所有这些视图的总称。
- `BufferSource` 是 `ArrayBuffer` 或 `ArrayBufferView` 的总称。
-我们将在下一章中了解这些术语。`BufferSource` 是最常用的术语之一,因为它的意思是“任何类型的二进制数据” — `ArrayBuffer` 或其上的视图。
+我们将在下一章中学习这些术语。`BufferSource` 是最常用的术语之一,因为它的意思是“任何类型的二进制数据” —— `ArrayBuffer` 或其上的视图。
这是一份备忘单: