Question

I'm working on a game and we make extensive use of typed arrays (Float32Arrays) for our math types. We save and load the gamestate from JSON. An example of JSON stringify output is for such an array (in Chrome) is:

"{"0":0,"1":0,"2":0,"length":3,"byteLength":12,"byteOffset":0,"buffer":{"byteLength":12}}"

This wastes space and causes them to be loaded as objects which is inconvenient. Ideally we could use the stringify 'replacer' function to test if a variable is a typed array, and then convert it to a bog standard array in that case. Unfortunately I'm not sure how to reliably test whether a variable is a typed array or not.

Any help?

Was it helpful?

Solution 2

If you're happy with it being a Float32Array or a subclass of Float32Array and they'll be from the same realm (loosely, window) as the code you're checking, see Anton's answer using instanceof.

If you need to know that it's specifically a Float32Array and not a subclass (and its from the same realm), you could use yourObject.constructor === Float32Array:

if (yourObject.constructor === Float32Array) {
     // It's a Float32Array
}

Live example:

if (typeof Float32Array === "undefined") {
  console.log("This browser doesn't support Float32Array");
} else {
  var array = new Float32Array(10);
  console.log(array.constructor === Float32Array); // true
}

But note that will fail if the object originates in a different realm (like another frame), because different environments have different Float32Array constructors (even though they do the same thing).

If you need to support cases where constructor won't work, you can use the Object.prototype.toString.call(yourObject) trick. That returns a useful string for all of the JavaScript built-in types ([object Array], [object Date], etc.) Per specification, Object.prototype.toString when applied to a typed array must return the string in the format "[object TypedArrayNameHere]".

So:

if (Object.prototype.toString.call(yourObject) === "[object Float32Array]") {
     // It's a Float32Array
}

Live example:

if (typeof Float32Array === "undefined") {
  console.log("This browser doesn't support Float32Array");
} else {
  console.log("Object.prototype.toString.call(new Float32Array()) returns: \"" +
    Object.prototype.toString.call(new Float32Array()) + "\"");
}

Note that it's possible to create objects that lie about their type, making Object.prototype.toString return the same thing it would return for (say) Float32Array:

const real = new Float32Array();
const fake = {
  get [Symbol.toStringTag]() {
    return "Float32Array";
  }
};
const realString = Object.prototype.toString.call(real);
const fakeString = Object.prototype.toString.call(fake);
console.log(realString);
console.log(fakeString);
console.log(realString === realString);

// You can also create a class that returns objects that lie:
class Foo {
  get [Symbol.toStringTag]() {
    return "Float32Array";
  }
}
const fake2 = new Foo();
console.log(Object.prototype.toString.call(fake2));

OTHER TIPS

ArrayBuffer.isView should help you out.

var data = [0,1,2]
var dataBuffer = new ArrayBuffer( data )
var dataBufferView = new Float32Array( data )

ArrayBuffer.isView(data) //false
ArrayBuffer.isView(dataBuffer) //false
ArrayBuffer.isView(dataBufferView) //true
dataBuffer instanceof ArrayBuffer //true

You also can use yourObject instanceof Float32Array construction. It returns true if your object is an instance of Float32Array and false in other case.

if (yourObject instanceof Float32Array) {
    // your code here
}

I've found a better way, if you want to test every possible TypedArray, according to MDN, you can get the TypedArray constructor. Using this constructor you can test if something is of its type:

var x = new Uint32Array();
var TypedArray = Object.getPrototypeOf(Uint8Array);
console.log(x instanceof TypedArray);

You can save this into a function like this:

const isTypedArray = (function() {
  const TypedArray = Object.getPrototypeOf(Uint8Array);
  return (obj) => obj instanceof TypedArray;
})();

I'm surprised no one got this one below. This should work in most cases to figure out if you have a typed array:

function isTypedArray(a) { return !!(a.buffer instanceof ArrayBuffer && a.BYTES_PER_ELEMENT); }

var a = [];
console.log(isTypedArray(a)); // (false);
var a = new Float32Array(3);
console.log(isTypedArray(a)); // (true);
var dataView = new DataView(a.buffer);
console.log(isTypedArray(dataView)); // (false);
console.log(isTypedArray(Float32Array)); // (false);

Of course this is "duck typing", and a instanceof Float32Array or similar is the best way to know for sure for specific types.

If you'd like a more general test that catches any of the ArrayBufferView and DataView types you can use:

if (Object.prototype.toString.call(yourObject.buffer) === "[object ArrayBuffer]") {
     // It's either an ArrayBufferView or a DataView
}

All typed arrays are inherit from ArrayBuffer. This type contains a byteLength property, so simple check if this property is available.

function isTypedArray(obj)
{
    return !!obj && obj.byteLength !== undefined;
}

Simple way to actually find all constructors that return a TypedArray:

isTypedArray = _ => _?.prototype?.__proto__?.constructor?.name == "TypedArray";
for (const key of Object.getOwnPropertyNames(window)) {
  const obj = window[key];
  if (isTypedArray(obj)) {
    console.log(key, "is a TypedArray");
  }
}

Output in current Chrome:

Uint8Array is a TypedArray
Int8Array is a TypedArray
Uint16Array is a TypedArray
Int16Array is a TypedArray
Uint32Array is a TypedArray
Int32Array is a TypedArray
Float32Array is a TypedArray
Float64Array is a TypedArray
Uint8ClampedArray is a TypedArray
BigUint64Array is a TypedArray
BigInt64Array is a TypedArray
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top