TypedArray
TypedArray 对象描述了底层二进制数据缓冲区的类数组视图
。没有称为 TypedArray 的全局属性,也没有直接可用的 TypedArray 构造函数。是所有 TypedArray 子类的通用父类。它只能通过子类对象原型链访问到,如:Object.getPrototypeOf(Int8Array)。
TypedArray子类对象
类型 | 值范围 | 字节大小 | 描述 | Web IDL 类型 | 等价的 C 类型 |
---|---|---|---|---|---|
Int8Array | -128 ~ 127 | 1 | 8 位有符号整型(补码) | byte | int8_t |
Uint8Array | 0 ~ 255 | 1 | 8 位无符号整型 | octet | uint8_t |
Uint8ClampedArray | 0 ~ 255 | 1 | 8 位无符号整型(一定在 0 ~ 255 之间) | octet | uint8_t |
Int16Array | -32768 ~ 32767 | 2 | 16 位有符号整型(补码) | short | int16_t |
Uint16Array | 0 ~ 65535 | 2 | 16 位无符号整型 | unsigned short | uint16_t |
Int32Array | -2147483648 ~ 2147483647 | 4 | 32 位有符号整型(补码) | long | int32_t |
Uint32Array | 0 ~ 4294967295 | 4 | 32 位无符号整型 | unsigned long | uint32_t |
Float32Array | -3.4E38 ~ 3.4E38 (最小正数1.2E-38) | 4 | 32 位 IEEE 浮点数(7 位有效数字) | unrestricted float | float |
Float64Array | -1.8E308 ~ 1.8E308 (最小正数5E-324) | 8 | 64 位 IEEE 浮点数(16 位有效数字) | unrestricted double | double |
BigInt64Array | -263 ~ 263 - 1 | 8 | 64 位有符号整型(补码) | bigint | int64_t |
BigUint64Array | 0 ~ 264 - 1 | 8 | 64 位无符号整型 | bigint | uint64_t |
TypedArray构造函数
- TypedArray本身是没有构造函数的,TypedArray是其子类的父类
- 该对象不能被直接实例化,试图去使用 new 构造它将会抛出TypeError。所有 TypedArray 子类的构造函数只能使用 new 构造。
下面 TypedArray 是一个具体的构造函数。可以理解为是TypedArray的任意子类(11个)
new TypedArray()
new TypedArray(object)
new TypedArray(length)
new TypedArray(typedArray)
new TypedArray(buffer)
new TypedArray(buffer, byteOffset)
new TypedArray(buffer, byteOffset, length)
new TypedArray()
new TypedArray(object)
new TypedArray(length)
new TypedArray(typedArray)
new TypedArray(buffer)
new TypedArray(buffer, byteOffset)
new TypedArray(buffer, byteOffset, length)
参数情况:
object
:当使用的不是 TypedArray 实例的对象调用时,与 TypedArray.from() 方法相同的方式创建一个新的类型化数组。
typedArray
:当使用 TypedArray 子类的实例调用时,typedArray 会被拷贝到一个新的类型数组中。
- 对于非 bigint TypeedArray 构造函数,typedArray 参数仅可以是非 bigint 类型(例如 Int32Array)。
- 对于 bigint TypedArray 构造函数(BigInt64Array 或 BigUint64Array),typedArray 参数仅可以是 bigint 类型。
- typedArray 中的每个值在拷贝到新数组之前都转换为构造函数的相应类型。新的类型化数组的长度与 typedArray 参数的长度相同。
length[可选]
: 当使用非对象调用时,该参数将被视为指定类型化数组长度的数字。在内存中创建一个内部数组缓冲区,大小长度乘以 BYTES_PER_ELEMENT 字节,用 0 填充。省略所有参数,等同于使用 0 作为参数。
buffer、byteOffset[可选]、length[可选]
: 当使用 ArrayBuffer 或 SharedArrayBuffer 实例以及可选的 byteOffset 和 length 参数调用时,将创建一个新的指定缓冲区的类型化数组视图。byteOffset 和 length 参数指定类型化数组视图将暴露的内存范围。
- 如果忽略这两个参数,则是整个视图的所有 buffer;
- 如果仅忽略 length,则是从 byteOffset 开始的 buffer 剩余部分的视图。
值编码与标准化
所有的`类型化数组`都是基于 ArrayBuffer 进行操作的,可以借此观察到每个元素的确切字节表示。因此二进制格式中的数字编码方式具有重要意义。- 无符号整数数组(Uint8Array、Uint16Array、Uint32Array 和 BigUint64Array)直接以二进制形式存储数字。
- 有符号整数数组(Int8Array、Int16Array、Int32Array 和 BigInt64Array)使用二进制补码存储数字。
- 浮点数组(Float32Array 和 Float64Array)使用 IEEE 754浮点格式存储数字。
- Uint8ClampedArray 是一种特殊情况。它像 Uint8Array 一样以二进制形式存储数字,但是当你存储超出范围的数字时,它会将数字钳制(clamp)到 0 到 255 的范围内,而不是截断最高有效位。
Int8Array、Unit8Array 和 Uint8ClampedArray 以外的其他类型数组都将每个元素存储为多个字节。这些字节可以按照从最高有效位到最低有效位(大端序)或从最低有效位到最高有效位(小端序)的顺序进行排序。类型化数组始终使用平台的本机字节顺序。如果要在缓冲区中写入和读取时指定字节顺序,应该使用 DataView。
当向这些类型化数组写入时,超出可表示范围的值将被标准化。
- 所有整数数组(Uint8ClampedArray 除外)都使用固定宽度数值转换,首先截断数字的小数部分,然后取最低位。
- Uint8ClampedArray 首先将数字钳制到 0 到 255 的范围内(大于 255 的值变为 255,小于 0 的值变为 0),然后使用银行家舍入法将结果四舍五入(而非向下取整)到最近的整数,也就是说,如果数字恰好在两个整数之间,它将四舍五入到最近的偶数。(例如,0.5 变为 0,1.5 变为 2,2.5 变为 2。)
- Float32Array 使用“银行家舍入法”将 64 位浮点数转换为 32 位。这与 Math.fround() 提供的算法相同。
底层为可变大小缓冲时的行为
当一个 TypedArray 被创建为一个可变大小缓冲的视图时,改变底层缓冲的大小会对 TypedArray 的大小产生不同的影响,这取决于 TypedArray 是否是长度跟踪的。
INFO
- 如果一个
类型化数组
是通过省略或传递 undefined 给第三个参数来创建的,那么它将跟踪长度,并且将自动调整大小以适应底层 buffer 的大小
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
const float32 = new Float32Array(buffer);
console.log(float32.byteLength); // 8
console.log(float32.length); // 2
buffer.resize(12);
console.log(float32.byteLength); // 12
console.log(float32.length); // 3
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
const float32 = new Float32Array(buffer);
console.log(float32.byteLength); // 8
console.log(float32.length); // 2
buffer.resize(12);
console.log(float32.byteLength); // 12
console.log(float32.length); // 3
- 如果一个类型化数组是通过使用第三个 length 参数指定大小创建时,则它的大小不会随着 buffer 大小的增长而改变:
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
const float32 = new Float32Array(buffer, 0, 2);
console.log(float32.byteLength); // 8
console.log(float32.length); // 2
console.log(float32[0]); // 0, 初始值
buffer.resize(12);
console.log(float32.byteLength); // 8
console.log(float32.length); // 2
console.log(float32[0]); // 0, 初始值
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
const float32 = new Float32Array(buffer, 0, 2);
console.log(float32.byteLength); // 8
console.log(float32.length); // 2
console.log(float32[0]); // 0, 初始值
buffer.resize(12);
console.log(float32.byteLength); // 8
console.log(float32.length); // 2
console.log(float32[0]); // 0, 初始值
- 当 buffer 缩小时,其上的类型化数组可能会超出范围,在这种情况下,类型化数组的观测大小将减少为 0。这是非长度跟踪类型化数组唯一可能改变长度的情况。如果你随后再次增长 buffer 以使类型化数组回到范围内,类型化数组的大小将恢复到其原始值。长度跟踪的类型化数组,如果 buffer 被缩小到小于 byteOffset,也会发生同样的事情。
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
const float32 = new Float32Array(buffer, 0, 2);
// buffer 缩小
buffer.resize(7);
console.log(float32.byteLength); // 0
console.log(float32.length); // 0
console.log(float32[0]); // undefinedv
// 恢复到其原始值
buffer.resize(8);
console.log(float32.byteLength); // 8
console.log(float32.length); // 2
console.log(float32[0]); // 0 - 又回到范围内了!
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
const float32 = new Float32Array(buffer, 0, 2);
// buffer 缩小
buffer.resize(7);
console.log(float32.byteLength); // 0
console.log(float32.length); // 0
console.log(float32[0]); // undefinedv
// 恢复到其原始值
buffer.resize(8);
console.log(float32.byteLength); // 8
console.log(float32.length); // 2
console.log(float32[0]); // 0 - 又回到范围内了!
- 长度跟踪的类型化数组,如果 buffer 被缩小到小于 byteOffset,也会发生同样的事情。
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
const float32 = new Float32Array(buffer, 4);
// float32 是长度跟踪的,但它只从第 4 个字节开始,直到缓冲的结尾,
// 所以如果缓冲被缩小到小于 4 个字节,类型化数组就将超出范围
buffer.resize(3);
console.log(float32.byteLength); // 0
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
const float32 = new Float32Array(buffer, 4);
// float32 是长度跟踪的,但它只从第 4 个字节开始,直到缓冲的结尾,
// 所以如果缓冲被缩小到小于 4 个字节,类型化数组就将超出范围
buffer.resize(3);
console.log(float32.byteLength); // 0
静态方法
TypedArray.from()
- 描述: 从一个类数组或者可迭代对象中创建一个新类型数组。这个方法和 Array.from() 类似。
TypedArray.from(arrayLike, mapFn)
TypedArray.from(arrayLike, mapFn, thisArg)
TypedArray.from(arrayLike, mapFn)
TypedArray.from(arrayLike, mapFn, thisArg)
该处TypedArray
指代它的所有子类。
- 参数:
arrayLike
:想要转换为类型数组的类数组或者可迭代对象。mapFn[可选]
:如果指定了该参数,则最后生成的类型数组会经过该函数的加工处理后再返回。thisArg[可选]
:执行 mapFn 函数时 this 的值。
- 返回:一个新的 TypedArray 实例。
Array.from() 和 TypedArray.from() 之间有一些微妙的区别:
- 如果 |this| 的值传递给 TypedArray.from 不是一个构造器,TypedArray.from 将抛出{jsxref("TypeError")}}, 而 Array.from 默认将创建一个 Array.
- TypedArray.from 使用[Put] 而 rray.from 使用[DefineProperty]]. 因此,当和 Proxy 对象一起时,它调用 handler.set 创建一个新的元素而非 handler.defineProperty.
- 当 from 获得一个迭代器时,TypedArray 一开始收集迭代器中的所有值,此时创建一个 |this| 的实例用于计数,然后在实例中设置值。 Array.from 设置每个从迭代器其中获取的值,最后设置它的长度。
- 当 Array.from 获得一个不可迭代的类数组时,it respects holes, 而 TypedArray.from 将确保结果是 dense
TypedArray.of()
- 描述:创建一个具有可变数量参数的新类型数组。此方法几乎与 Array.of() 相同。
TypedArray.of(element0)
TypedArray.of(element0, element1)
TypedArray.of(element0, element1, /* ... ,*/ elementN)
TypedArray.of(element0)
TypedArray.of(element0, element1)
TypedArray.of(element0, element1, /* ... ,*/ elementN)
Uint8Array.of(1); // Uint8Array [ 1 ]
Int8Array.of("1", "2", "3"); // Int8Array [ 1, 2, 3 ]
Float32Array.of(1, 2, 3); // Float32Array [ 1, 2, 3 ]
Int16Array.of(undefined); // IntArray [ 0 ]
Uint8Array.of(1); // Uint8Array [ 1 ]
Int8Array.of("1", "2", "3"); // Int8Array [ 1, 2, 3 ]
Float32Array.of(1, 2, 3); // Float32Array [ 1, 2, 3 ]
Int16Array.of(undefined); // IntArray [ 0 ]
实例属性
TypedArray.prototype.buffer
- 描述:表示由 TypedArray 在构造期间引用的 ArrayBuffer。
TypedArray.prototype.byteLength
- 描述:返回该类型化数组的长度(字节数)。
TypedArray.prototype.byteOffset
- 描述:返回该类型化数组距离其ArrayBuffer起始位置的偏移(字节数)
TypedArray.prototype.length
- 描述:返回该类型化数组的长度(以元素为单位)。