Much data on the web is text: JSON files, HTML files, CSS files, JavaScript code, etc. JavaScript handles such data well via its built-in strings.
However, before 2011, it did not handle binary data well. The Typed Array Specification 1.0 was introduced on February 8, 2011 and provides tools for working with binary data. With ECMAScript 6, Typed Arrays were added to the core language and gained methods that were previously only available for normal Arrays (.map()
, .filter()
, etc.).
The main uses cases for Typed Arrays, are:
Processing binary data: managing image data, manipulating binary files, handling binary network protocols, etc.
Interacting with native APIs: Native APIs often receive and return data in a binary format, which we could neither store nor manipulate well in pre-ES6 JavaScript. That meant that whenever we were communicating with such an API, data had to be converted from JavaScript to binary and back for every call. Typed Arrays eliminate this bottleneck. Examples include:
WebGL, “a low-level 3D graphics API based on OpenGL ES, exposed to ECMAScript via the HTML5 Canvas element”. Typed Arrays were initially created for WebGL. Section “History of Typed Arrays” of the article “Typed Arrays: Binary Data in the Browser” (by Ilmari Heikkinen for HTML5 Rocks) has more information.
WebGPU, “an API for performing operations, such as rendering and computation, on a Graphics Processing Unit”. For example, WebGPU uses ArrayBuffers as wrappers for backing stores.
WebAssembly (short: “Wasm”), “a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.” For example, the memory of WebAssembly code is stored in an ArrayBuffer or a SharedArrayBuffer (details).
ArrayBuffer
, Typed Arrays, DataView
The Typed Array API stores binary data in instances of ArrayBuffer
:
const buf = new ArrayBuffer(4); // length in bytes
// buf is initialized with zeros
An ArrayBuffer itself is a black box: if we want to access its data, we must wrap it in another object – a view object. Two kinds of view objects are available:
Typed Arrays: let us access the data as an indexed sequence of elements that all have the same type. Examples include:
Uint8Array
: Elements are unsigned 8-bit integers. Unsigned means that their ranges start at zero.
Int16Array
: Elements are signed 16-bit integers. Signed means that they have a sign and can be negative, zero, or positive.
Float32Array
: Elements are 32-bit floating point numbers.
DataViews: let us interpret the data as various types (Uint8
, Int16
, Float32
, etc.) that we can read and write at any byte offset.
Figure 34.1 shows a class diagram of the API.
SharedArrayBuffer
[ES2017]SharedArrayBuffer is an ArrayBuffer whose memory can be accessed by multiple agents (an agent being the main thread or a web worker) concurrently.
Atomics
is a global namespace for an API that complements SharedArrayBuffers. The ECMAScript specification describes it as “functions that operate indivisibly (atomically) on shared memory array cells as well as functions that let agents wait for and dispatch primitive events. When used with discipline, the Atomics functions allow multi-agent programs that communicate through shared memory to execute in a well-understood order even on parallel CPUs.”
See MDN Web Docs for more information on SharedArrayBuffer
and Atomics
.
Typed Arrays are used much like normal Arrays.
The following code shows three different ways of creating the same Typed Array:
// Argument: Typed Array or Array-like object
const ta1 = new Uint8Array([0, 1, 2]);
const ta2 = Uint8Array.of(0, 1, 2);
const ta3 = new Uint8Array(3); // length of Typed Array
ta3[0] = 0;
ta3[1] = 1;
ta3[2] = 2;
assert.deepEqual(ta1, ta2);
assert.deepEqual(ta1, ta3);
const typedArray = new Int16Array(2); // 2 elements
assert.equal(typedArray.length, 2);
assert.deepEqual(
typedArray.buffer, new ArrayBuffer(4)); // 4 bytes
const typedArray = new Int16Array(2);
assert.equal(typedArray[1], 0); // initialized with 0
typedArray[1] = 72;
assert.equal(typedArray[1], 72);
Typed Arrays don’t have a method .concat()
, like normal Arrays do. The workaround is to use their overloaded method .set()
:
.set(typedArray: TypedArray, offset=0): void
.set(arrayLike: ArrayLike<number>, offset=0): void
It copies the existing typedArray
or arrayLike
into the receiver, at index offset
. TypedArray
is an internal abstract superclass of all concrete Typed Array classes (that doesn’t actually have a global name).
The following function uses that method to copy zero or more Typed Arrays (or Array-like objects) into an instance of resultConstructor
:
function concatenate(resultConstructor, ...arrays) {
let totalLength = 0;
for (const arr of arrays) {
totalLength += arr.length;
}
const result = new resultConstructor(totalLength);
let offset = 0;
for (const arr of arrays) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
assert.deepEqual(
concatenate(Uint8Array, Uint8Array.of(1, 2), [3, 4]),
Uint8Array.of(1, 2, 3, 4));
Typed Arrays are much like normal Arrays: they have a .length
, elements can be accessed via the bracket operator []
, and they have most of the standard Array methods. They differ from normal Arrays in the following ways:
Typed Arrays have buffers. The elements of a Typed Array ta
are not stored in ta
, they are stored in an associated ArrayBuffer that can be accessed via ta.buffer
:
const ta = new Uint16Array(2); // 2 elements
assert.deepEqual(
ta.buffer, new ArrayBuffer(4)); // 4 bytes
Typed Arrays are initialized with zeros:
new Array(4)
creates a normal Array without any elements. It only has four holes (indices less than the .length
that have no associated elements).
new Uint8Array(4)
creates a Typed Array whose four elements are all 0.
assert.deepEqual(new Uint8Array(4), Uint8Array.of(0, 0, 0, 0));
All of the elements of a Typed Array have the same type:
Setting elements converts values to that type.
const ta = new Uint8Array(1);
ta[0] = 257;
assert.equal(ta[0], 1); // 257 % 256 (overflow)
ta[0] = '2';
assert.equal(ta[0], 2);
Getting elements returns numbers or bigints.
const ta = new Uint8Array(1);
assert.equal(ta[0], 0);
assert.equal(typeof ta[0], 'number');
The .length
of a Typed Array is derived from its ArrayBuffer and never changes (unless we switch to a different ArrayBuffer).
Normal Arrays can have holes; Typed Arrays can’t.
This is how DataViews are used:
const dataView = new DataView(new ArrayBuffer(4));
assert.equal(dataView.getInt16(0), 0);
assert.equal(dataView.getUint8(0), 0);
dataView.setUint8(0, 5);
Element | Typed Array | Bytes | Description | Get/Set | |
---|---|---|---|---|---|
Int8 | Int8Array | 1 | 8-bit signed integer | number | ES6 |
Uint8 | Uint8Array | 1 | 8-bit unsigned int | number | ES6 |
Uint8C | Uint8ClampedArray | 1 | 8-bit unsigned int | number | ES6 |
(clamped conv.) | number | ES6 | |||
Int16 | Int16Array | 2 | 16-bit signed int | number | ES6 |
Uint16 | Uint16Array | 2 | 16-bit unsigned int | number | ES6 |
Int32 | Int32Array | 4 | 32-bit signed int | number | ES6 |
Uint32 | Uint32Array | 4 | 32-bit unsigned int | number | ES6 |
BigInt64 | BigInt64Array | 8 | 64-bit signed int | bigint | ES2020 |
BigUint64 | BigUint64Array | 8 | 64-bit unsigned int | bigint | ES2020 |
Float32 | Float32Array | 4 | 32-bit floating point | number | ES6 |
Float64 | Float64Array | 8 | 64-bit floating point | number | ES6 |
Table 34.1: Element types supported by the Typed Array API.
Table 34.1 lists the available element types. These types (e.g., Int32
) show up in two locations:
In Typed Arrays, they specify the types of the elements. For example, all elements of a Int32Array
have the type Int32
. The element type is the only aspect of Typed Arrays that differs.
In DataViews, they are the lenses through which they access their ArrayBuffers when we use methods such as .getInt32()
and .setInt32()
.
The element type Uint8C
is special: it is not supported by DataView
and only exists to enable Uint8ClampedArray
. This Typed Array is used by the canvas
element (where it replaces CanvasPixelArray
) and should otherwise be avoided. The only difference between Uint8C
and Uint8
is how overflow and underflow are handled (as explained in the next subsection).
Typed Arrays and Array Buffers use numbers and bigints to import and export values:
The types BigInt64
and BigUint64
are handled via bigints. For example, setters accept bigints and getters return bigints.
All other element types are handled via numbers.
Normally, when a value is out of the range of the element type, modulo arithmetic is used to convert it to a value within range. For signed and unsigned integers that means that:
The following function helps illustrate how conversion works:
function setAndGet(typedArray, value) {
typedArray[0] = value;
return typedArray[0];
}
Modulo conversion for unsigned 8-bit integers:
const uint8 = new Uint8Array(1);
// Highest value of range
assert.equal(setAndGet(uint8, 255), 255);
// Overflow
assert.equal(setAndGet(uint8, 256), 0);
// Lowest value of range
assert.equal(setAndGet(uint8, 0), 0);
// Underflow
assert.equal(setAndGet(uint8, -1), 255);
Modulo conversion for signed 8-bit integers:
const int8 = new Int8Array(1);
// Highest value of range
assert.equal(setAndGet(int8, 127), 127);
// Overflow
assert.equal(setAndGet(int8, 128), -128);
// Lowest value of range
assert.equal(setAndGet(int8, -128), -128);
// Underflow
assert.equal(setAndGet(int8, -129), 127);
Clamped conversion is different:
const uint8c = new Uint8ClampedArray(1);
// Highest value of range
assert.equal(setAndGet(uint8c, 255), 255);
// Overflow
assert.equal(setAndGet(uint8c, 256), 255);
// Lowest value of range
assert.equal(setAndGet(uint8c, 0), 0);
// Underflow
assert.equal(setAndGet(uint8c, -1), 0);
Whenever a type (such as Uint16
) is stored as a sequence of multiple bytes, endianness matters:
Uint16
value 0x4321 is stored as two bytes – first 0x43, then 0x21.
Uint16
value 0x4321 is stored as two bytes – first 0x21, then 0x43.
Endianness tends to be fixed per CPU architecture and consistent across native APIs. Typed Arrays are used to communicate with those APIs, which is why their endianness follows the endianness of the platform and can’t be changed.
On the other hand, the endianness of protocols and binary files varies, but is fixed per format, across platforms. Therefore, we must be able to access data with either endianness. DataViews serve this use case and let us specify endianness when we get or set a value.
Quoting Wikipedia on Endianness:
Other orderings are also possible. Those are generically called middle-endian or mixed-endian.
In this section, «ElementType»Array
stands for Int8Array
, Uint8Array
, etc. ElementType
is Int8
, Uint8
, etc.
«ElementType»Array.from()
This method has the type signature:
.from<S>(
source: Iterable<S>|ArrayLike<S>,
mapfn?: S => ElementType, thisArg?: any)
: «ElementType»Array
.from()
converts source
into an instance of this
(a Typed Array).
For example, normal Arrays are iterable and can be converted with this method:
assert.deepEqual(
Uint16Array.from([0, 1, 2]),
Uint16Array.of(0, 1, 2));
Typed Arrays are also iterable:
assert.deepEqual(
Uint16Array.from(Uint8Array.of(0, 1, 2)),
Uint16Array.of(0, 1, 2));
source
can also be an Array-like object:
assert.deepEqual(
Uint16Array.from({0:0, 1:1, 2:2, length: 3}),
Uint16Array.of(0, 1, 2));
The optional mapfn
lets us transform the elements of source
before they become elements of the result. Why perform the two steps mapping and conversion in one go? Compared to mapping separately via .map()
, there are two advantages:
Read on for an explanation of the second advantage.
The static method .from()
can optionally both map and convert between Typed Array types. Less can go wrong if we use that method.
To see why that is, let us first convert a Typed Array to a Typed Array with a higher precision. If we use .from()
to map, the result is automatically correct. Otherwise, we must first convert and then map.
const typedArray = Int8Array.of(127, 126, 125);
assert.deepEqual(
Int16Array.from(typedArray, x => x * 2),
Int16Array.of(254, 252, 250));
assert.deepEqual(
Int16Array.from(typedArray).map(x => x * 2),
Int16Array.of(254, 252, 250)); // OK
assert.deepEqual(
Int16Array.from(typedArray.map(x => x * 2)),
Int16Array.of(-2, -4, -6)); // wrong
If we go from a Typed Array to a Typed Array with a lower precision, mapping via .from()
produces the correct result. Otherwise, we must first map and then convert.
assert.deepEqual(
Int8Array.from(Int16Array.of(254, 252, 250), x => x / 2),
Int8Array.of(127, 126, 125));
assert.deepEqual(
Int8Array.from(Int16Array.of(254, 252, 250).map(x => x / 2)),
Int8Array.of(127, 126, 125)); // OK
assert.deepEqual(
Int8Array.from(Int16Array.of(254, 252, 250)).map(x => x / 2),
Int8Array.of(-1, -2, -3)); // wrong
The problem is that if we map via .map()
, then input type and output type are the same. In contrast, .from()
goes from an arbitrary input type to an output type that we specify via its receiver.
Typed Arrays are iterable. That means that we can use the for-of
loop and other iteration-based mechanisms:
const ui8 = Uint8Array.of(0, 1, 2);
for (const byte of ui8) {
console.log(byte);
}
Output:
0
1
2
ArrayBuffers and DataViews are not iterable.
To convert a normal Array to a Typed Array, we pass it to:
«ElementType»Array.from()
– which accepts iterable values and Array-like values.
For example:
const ta1 = new Uint8Array([0, 1, 2]);
const ta2 = Uint8Array.from([0, 1, 2]);
assert.deepEqual(ta1, ta2);
To convert a Typed Array to a normal Array, we can use Array.from()
or spreading (because Typed Arrays are iterable):
assert.deepEqual(
[...Uint8Array.of(0, 1, 2)], [0, 1, 2]
);
assert.deepEqual(
Array.from(Uint8Array.of(0, 1, 2)), [0, 1, 2]
);
Before ArrayBuffers became resizable, they had fixed sizes. If we wanted one to grow or shrink, we had to allocate a new one and copy the old one over. That costs time and can fragment the address space on 32-bit systems.
These are the changes introduced by resizing:
The existing constructor gets one more parameter:
new ArrayBuffer(byteLength: number, options?: {maxByteLength?: number})
There is one new method and two new getters:
ArrayBuffer.prototype.resize(newByteLength: number)
get ArrayBuffer.prototype.resizable
get ArrayBuffer.prototype.maxByteLength
options.maxByteLength
if it was provided to the constructor. Otherwise, it returns this.byteLength
.
The existing method .slice()
always returns non-resizable ArrayBuffers.
The options
object of the constructor determines whether or not an ArrayBuffer is resizable:
const resizableArrayBuffer = new ArrayBuffer(16, {maxByteLength: 32});
assert.equal(
resizableArrayBuffer.resizable, true
);
const fixedArrayBuffer = new ArrayBuffer(16);
assert.equal(
fixedArrayBuffer.resizable, false
);
This is what constructors of Typed Arrays look like:
new «TypedArray»(
buffer: ArrayBuffer | SharedArrayBuffer,
byteOffset?: number,
length?: number
)
If length
is undefined
then the .length
and .byteLength
of the Typed Array instance automatically tracks the length of a resizable buffer
:
const buf = new ArrayBuffer(2, {maxByteLength: 4});
// `tarr1` starts at offset 0 (`length` is undefined)
const tarr1 = new Uint8Array(buf);
// `tarr2` starts at offset 2 (`length` is undefined)
const tarr2 = new Uint8Array(buf, 2);
assert.equal(
tarr1.length, 2
);
assert.equal(
tarr2.length, 0
);
buf.resize(4);
assert.equal(
tarr1.length, 4
);
assert.equal(
tarr2.length, 2
);
If an ArrayBuffer is resized then a wrapper with a fixed length can go out of bounds: The wrapper’s range isn’t covered by the ArrayBuffer anymore. That is treated by JavaScript as if the ArrayBuffer were detached:
.length
, .byteLength
and .byteOffset
are zero.
undefined
.
const buf = new ArrayBuffer(4, {maxByteLength: 4});
const tarr = new Uint8Array(buf, 2, 2);
assert.equal(
tarr.length, 2
);
buf.resize(3);
// `tarr` is now partially out of bounds
assert.equal(
tarr.length, 0
);
assert.equal(
tarr.byteLength, 0
);
assert.equal(
tarr.byteOffset, 0
);
assert.equal(
tarr[0], undefined
);
assert.throws(
() => tarr.at(0),
/^TypeError: Cannot perform %TypedArray%.prototype.at on a detached ArrayBuffer$/
);
The ECMAScript specification gives the following guidelines for working with resizable ArrayBuffers:
We recommend that programs be tested in their deployment environments where possible. The amount of available physical memory differs greatly between hardware devices. Similarly, virtual memory subsystems also differ greatly between hardware devices as well as operating systems. An application that runs without out-of-memory errors on a 64-bit desktop web browser could run out of memory on a 32-bit mobile web browser.
When choosing a value for the maxByteLength
option for resizable ArrayBuffer, we recommend that the smallest possible size for the application be chosen. We recommend that maxByteLength
does not exceed 1,073,741,824 (230 bytes or 1 GiB).
Please note that successfully constructing a resizable ArrayBuffer for a particular maximum size does not guarantee that future resizes will succeed.
The web API (not the ECMAScript standard) has long supported structured cloning for safely moving values across realms (globalThis
, iframes, web workers, etc.). Some objects can also be transferred: After cloning, the original becomes detached (inaccessible) and ownership switches from the original to the clone. Transfering is usually faster than copying, especially if large amounts of memory are involved. These are the most common classes of transferable objects:
ArrayBuffer
ReadableStream
TransformStream
WritableStream
ImageBitmap
OffscreenCanvas
MessagePort
RTCDataChannel
ArrayBuffer.prototype.transfer(newLength?: number)
ArrayBuffer.prototype.transferToFixedLength(newLength?: number)
get ArrayBuffer.prototype.detached
structuredClone()
The broadly supported structuredClone()
also lets us transfer (and therefore detach) ArrayBuffers:
const original = new ArrayBuffer(16);
const clone = structuredClone(original, {transfer: [original]});
assert.equal(
original.byteLength, 0
);
assert.equal(
clone.byteLength, 16
);
assert.equal(
original.detached, true
);
assert.equal(
clone.detached, false
);
The ArrayBuffer method .transfer()
simply gives us a more concise way to detach an ArrayBuffer:
const original = new ArrayBuffer(16);
const transferred = original.transfer();
assert.equal(
original.detached, true
);
assert.equal(
transferred.detached, false
);
Transferring is most often used between two agents (an agent being the main thread or a web worker). However, transferring within the same agent can make sense too: If a function gets a (potentially shared) ArrayBuffer as a parameter, it can transfer it so that no external code can interfere with what it does. Example (taken from the ECMAScript proposal and slightly edited):
async function validateAndWriteSafeAndFast(arrayBuffer) {
const owned = arrayBuffer.transfer();
// We have `owned` and no one can access its data via
// `arrayBuffer` now because the latter is detached:
assert.equal(
arrayBuffer.detached, true
);
// `await` pauses this function – which gives external
// code the opportunity to access `arrayBuffer`.
await validate(owned);
await fs.writeFile("data.bin", owned);
}
Preparation:
> const arrayBuffer = new ArrayBuffer(16);
> const typedArray = new Uint8Array(arrayBuffer);
> arrayBuffer.transfer();
Lengths and offsets are all zero:
> typedArray.length
0
> typedArray.byteLength
0
> typedArray.byteOffset
0
Getting elements returns undefined
; setting elements fails silently:
> typedArray[0]
undefined
> typedArray[0] = 128
128
All element-related methods throw exceptions:
> typedArray.at(0)
TypeError: Cannot perform %TypedArray%.prototype.at on a detached ArrayBuffer
All data-related methods of DataViews throw:
> const arrayBuffer = new ArrayBuffer(16);
> const dataView = new DataView(arrayBuffer);
> arrayBuffer.transfer();
> dataView.byteLength
TypeError: Cannot perform get DataView.prototype.byteLength on a detached ArrayBuffer
> dataView.getUint8(0)
TypeError: Cannot perform DataView.prototype.getUint8 on a detached ArrayBuffer
> const arrayBuffer = new ArrayBuffer(16);
> arrayBuffer.transfer();
> new Uint8Array(arrayBuffer)
TypeError: Cannot perform Construct on a detached ArrayBuffer
> new DataView(arrayBuffer)
TypeError: Cannot perform DataView constructor on a detached ArrayBuffer
ArrayBuffer.prototype.transferToFixedLength()
This method rounds out the API: It transfers and converts a resizable ArrayBuffer to one with a fixed length. That may free up memory that was held in preparation for growth.
In preparation for the quick references on ArrayBuffers, Typed Arrays, and DataViews, we need learn the differences between indices and offsets:
Indices for the bracket operator [ ]
: We can only use non-negative indices (starting at 0).
In normal Arrays, writing to negative indices creates properties:
const arr = [6, 7];
arr[-1] = 5;
assert.deepEqual(
Object.keys(arr), ['0', '1', '-1']);
In Typed Arrays, writing to negative indices is ignored:
const tarr = Uint8Array.of(6, 7);
tarr[-1] = 5;
assert.deepEqual(
Object.keys(tarr), ['0', '1']);
Indices for methods of ArrayBuffers, Typed Arrays, and DataViews: Every index can be negative. If it is, it is added to the length of the entity to produce the actual index. Therefore, -1
refers to the last element, -2
to the second-last, etc. Methods of normal Arrays work the same way.
const ui8 = Uint8Array.of(0, 1, 2);
assert.deepEqual(ui8.slice(-1), Uint8Array.of(2));
Offsets passed to methods of Typed Arrays and DataViews: must be non-negative – for example:
const dataView = new DataView(new ArrayBuffer(4));
assert.throws(
() => dataView.getUint8(-1),
{
name: 'RangeError',
message: 'Offset is outside the bounds of the DataView',
});
Whether a parameter is an index or an offset can only be determined by looking at documentation; there is no simple rule.
ArrayBuffers store binary data, which is meant to be accessed via Typed Arrays and DataViews.
new ArrayBuffer()
new ArrayBuffer(byteLength, options?)
[ES6]
new ArrayBuffer(
byteLength: number,
options?: { // ES2024
maxByteLength?: number
}
)
Invoking this constructor via new
creates an instance whose capacity is length
bytes. Each of those bytes is initially 0.
If options.maxByteLength
is provided, the ArrayBuffer can be resized. Otherwise, it has a fixed length.
ArrayBuffer.*
ArrayBuffer.isView(arg)
[ES6]
Returns true
if arg
is a view for an ArrayBuffer (i.e., if it is a Typed Array or a DataView).
> ArrayBuffer.isView(new Uint8Array())
true
> ArrayBuffer.isView(new DataView(new ArrayBuffer()))
true
ArrayBuffer.prototype.*
: getting and slicingget ArrayBuffer.prototype.byteLength
[ES6]
Returns the capacity of this ArrayBuffer in bytes.
ArrayBuffer.prototype.slice(startIndex=0, endIndex=this.byteLength)
[ES6]
Creates a new ArrayBuffer that contains the bytes of this ArrayBuffer whose indices are greater than or equal to startIndex
and less than endIndex
. start
and endIndex
can be negative (see “Quick references: indices vs. offsets” (§34.8)).
ArrayBuffer.prototype.*
: resizingArrayBuffer.prototype.resize(newByteLength)
[ES2024]
Changes the size of this ArrayBuffer. For more information, see “Resizing ArrayBuffers” (§34.6).
get ArrayBuffer.prototype.resizable
[ES2024]
Returns true
if this ArrayBuffer is resizable and false
if it is not.
get ArrayBuffer.prototype.maxByteLength
[ES2024]
Returns options.maxByteLength
if it was provided to the constructor. Otherwise, it returns this.byteLength
.
The properties of the various Typed Array objects are introduced in two steps:
TypedArray
: First, we look at the abstract superclass of all Typed Array classes (which was shown in the class diagram at the beginning of this chapter). That superclass is called TypedArray
but it does not have a global name in JavaScript:
> Object.getPrototypeOf(Uint8Array).name
'TypedArray'
«ElementType»Array
: The concrete Typed Array classes are called Uint8Array
, Int16Array
, Float32Array
, etc. These are the classes that we use via new
, .of
, and .from()
.
TypedArray.*
Both static TypedArray
methods are inherited by its subclasses (Uint8Array
, etc.). Therefore, we can use these methods via the subclasses, which are concrete and can have direct instances.
TypedArray.from(iterableOrArrayLike, mapFunc?)
[ES6]
// BigInt64Array: bigint instead of number
TypedArray.from<T>(
iterableOrArrayLike: Iterable<number> | ArrayLike<number>
): TypedArray<T>
TypedArray.from<S, T>(
iterableOrArrayLike: Iterable<S> | ArrayLike<S>,
mapFunc: (v: S, k: number) => T, thisArg?: any
): TypedArray<T>
Converts an iterable (including Arrays and Typed Arrays) or an Array-like object to an instance of the Typed Array class.
assert.deepEqual(
Uint16Array.from([0, 1, 2]),
Uint16Array.of(0, 1, 2));
The optional mapFunc
lets us transform the elements of source
before they become elements of the result.
assert.deepEqual(
Int16Array.from(Int8Array.of(127, 126, 125), x => x * 2),
Int16Array.of(254, 252, 250));
TypedArray.of(...items)
[ES6]
// BigInt64Array: bigint instead of number
TypedArray.of<T>(
...items: Array<number>
): TypedArray<T>
Creates a new instance of the Typed Array class whose elements are items
(coerced to the element type).
assert.deepEqual(
Int16Array.of(-1234, 5, 67),
new Int16Array([-1234, 5, 67]) );
TypedArray.prototype.*
Indices accepted by Typed Array methods can be negative (they work like traditional Array methods that way). Offsets must be non-negative. For details, see “Quick references: indices vs. offsets” (§34.8).
The following properties are specific to Typed Arrays; normal Arrays don’t have them:
get TypedArray.prototype.buffer
Returns the ArrayBuffer backing this Typed Array.
get TypedArray.prototype.length
Returns the length in elements of this Typed Array’s buffer.
> new Uint32Array(new ArrayBuffer(4)).length
1
get TypedArray.prototype.byteLength
Returns the size in bytes of this Typed Array’s buffer.
> new Uint32Array(new ArrayBuffer(4)).byteLength
4
get TypedArray.prototype.byteOffset
Returns the offset where this Typed Array “starts” inside its ArrayBuffer.
TypedArray.prototype.set(typedArrayOrArrayLike, offset=0)
Copies all elements of the first parameter to this Typed Array. The element at index 0 of the parameter is written to index offset
of this Typed Array (etc.). For more information on Array-like objects, consult “Array-like objects” (§33.5).
TypedArray.prototype.subarray(startIndex=0, endIndex=this.length)
Returns a new Typed Array that has the same buffer as this Typed Array, but a (generally) smaller range. If startIndex
is non-negative then the first element of the resulting Typed Array is this[startIndex]
, the second this[startIndex+1]
(etc.). If startIndex
in negative, it is converted appropriately.
The following methods are basically the same as the methods of normal Arrays (the ECMAScript versions specify when the methods were added to Arrays – Typed Arrays didn’t exist in ECMAScript before ES6):
TypedArray.prototype.at(index)
[R, ES2022]
TypedArray.prototype.copyWithin(target, start, end=this.length)
[W, ES6]
TypedArray.prototype.entries()
[R, ES6]
TypedArray.prototype.every(predicate, thisArg?)
[R, ES5]
TypedArray.prototype.fill(start=0, end=this.length)
[W, ES6]
TypedArray.prototype.filter(predicate, thisArg?)
[R, ES5]
TypedArray.prototype.find(predicate, thisArg?)
[R, ES6]
TypedArray.prototype.findIndex(predicate, thisArg?)
[R, ES6]
TypedArray.prototype.findLast(predicate, thisArg?)
[R, ES2023]
TypedArray.prototype.findLastIndex(predicate, thisArg?)
[R, ES2023]
TypedArray.prototype.forEach(callback)
[R, ES5]
TypedArray.prototype.includes(searchElement, fromIndex)
[R, ES2016]
TypedArray.prototype.indexOf(searchElement, fromIndex)
[R, ES5]
TypedArray.prototype.join(separator = ',')
[R, ES1]
TypedArray.prototype.keys()
[R, ES6]
TypedArray.prototype.lastIndexOf(searchElement, fromIndex)
[R, ES5]
TypedArray.prototype.map(callback, thisArg?)
[R, ES5]
TypedArray.prototype.reduce(callback, initialValue?)
[R, ES5]
TypedArray.prototype.reduceRight(callback, initialValue?)
[R, ES5]
TypedArray.prototype.reverse()
[W, ES1]
TypedArray.prototype.slice(start?, end?)
[R, ES3]
TypedArray.prototype.some(predicate, thisArg?)
[R, ES5]
TypedArray.prototype.sort(compareFunc?)
[W, ES1]
TypedArray.prototype.toLocaleString()
[R, ES3]
TypedArray.prototype.toReversed()
[R, ES2023]
TypedArray.prototype.toSorted(compareFunc?)
[R, ES2023]
TypedArray.prototype.toSpliced(start?, deleteCount?, ...items)
[R, ES2023]
TypedArray.prototype.toString()
[R, ES1]
TypedArray.prototype.values()
[R, ES6]
TypedArray.prototype.with(index, value)
[R, ES2023]
For details on how these methods work, see “Quick reference: Array
” (§33.17).
new «ElementType»Array()
Each Typed Array constructor has a name that follows the pattern «ElementType»Array
, where «ElementType»
is one of the element types listed in Table 34.1. That means there are 11 constructors for Typed Arrays:
Float32Array
, Float64Array
Int8Array
, Int16Array
, Int32Array
BigInt64Array
Uint8Array
, Uint8ClampedArray
, Uint16Array
, Uint32Array
BigUint64Array
Each constructor has several overloaded versions – it behaves differently depending on how many arguments it receives and what their types are:
new «ElementType»Array(length=0)
Creates a new «ElementType»Array
with the given length
and the appropriate buffer. The buffer’s size in bytes is:
length * «ElementType»Array.BYTES_PER_ELEMENT
new «ElementType»Array(source: TypedArray)
Creates a new instance of «ElementType»Array
whose elements have the same values as the elements of source
, but coerced to ElementType
.
new «ElementType»Array(source: Iterable<number>)
BigInt64Array
, BigUint64Array
: bigint
instead of number
Creates a new instance of «ElementType»Array
whose elements have the same values as the items of source
, but coerced to ElementType
. For more information on iterables, see “Synchronous iteration” (§32).
new «ElementType»Array(source: ArrayLike<number>)
BigInt64Array
, BigUint64Array
: bigint
instead of number
Creates a new instance of «ElementType»Array
whose elements have the same values as the elements of source
, but coerced to ElementType
. For more information on Array-like objects, see “Array-like objects” (§33.5).
new «ElementType»Array(buffer: ArrayBuffer, byteOffset=0, length=0)
Creates a new «ElementType»Array
whose buffer is buffer
. It starts accessing the buffer at the given byteOffset
and will have the given length
. Note that length
counts elements of the Typed Array (with 1–8 bytes each), not bytes.
«ElementType»Array.*
«ElementType»Array.BYTES_PER_ELEMENT: number
Counts how many bytes are needed to store a single element:
> Uint8Array.BYTES_PER_ELEMENT
1
> Int16Array.BYTES_PER_ELEMENT
2
> Float64Array.BYTES_PER_ELEMENT
8
«ElementType»Array.prototype.*
«ElementType»Array.prototype.BYTES_PER_ELEMENT: number
The same as «ElementType»Array.BYTES_PER_ELEMENT
.
new DataView()
new DataView(arrayBuffer, byteOffset?, byteLength?)
[ES6]
Creates a new DataView whose data is stored in the ArrayBuffer buffer
. By default, the new DataView can access all of buffer
. The last two parameters allow us to change that.
DataView.prototype.*
In the remainder of this section, «ElementType»
refers to either:
Int8
, Int16
, Int32
, BigInt64
Uint8
, Uint16
, Uint32
, BigUint64
Float32
, Float64
These are the properties of DataView.prototype
:
get DataView.prototype.buffer
[ES6]
Returns the ArrayBuffer of this DataView.
get DataView.prototype.byteLength
[ES6]
Returns how many bytes can be accessed by this DataView.
get DataView.prototype.byteOffset
[ES6]
Returns at which offset this DataView starts accessing the bytes in its buffer.
DataView.prototype.get«ElementType»(byteOffset, littleEndian=false)
[ES6]
Returns:
BigInt64
, BigUint64
: bigint
number
Reads a value from the buffer of this DataView.
DataView.prototype.set«ElementType»(byteOffset, value, littleEndian=false)
[ES6]
Type of value
:
BigInt64
, BigUint64
: bigint
number
Writes value
to the buffer of this DataView.