Categories
Modern JavaScript

Best of Modern JavaScript — Typed Arrays and Map/Set Issues

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at issues with maps, sets, and typed arrays.

Why do Maps and Sets have the size Property?

Maps and sets have the size property instead of length because they aren’t sequential data structures, unlike arrays.

length is meant for sequential data structures like arrays.

Configure How Maps and Sets Compare Keys and Values

There’s no way to configure how maps and sets compare their keys and values.

This feature is hard to implement efficiently and properly.

Specify a Default Value when Getting Something out of a Map

There’s no short to specify a default value when we get something out of a map.

Maps don’t let us set the default value directly.

But we can use the || operator to do this since get returns undefined when the entry doesn’t exist.

Therefore, we can write:

const map = new Map();
//...
const prevCount = map.get('foo') || 0;

We get the value with the key 'foo' .

If it’s undefined , we return 0.

Map vs Object

Maps are good for holding keys other than string keys.

Otherwise, objects can give us equivalent functionality.

If we’re mapping arbitrary data, then a map is probably a better choice.

If there’s a fixed set of keys, then objects are a good choice.

If the keys change, then maps are better.

When to use Objects as Map Keys?

We can use objects as map keys if we externally attach data to objects.

But this is better done with WeakMaps since garbage collection happens if we remove the reference to the key object.

Typed Arrays

Typed arrays are a special kind of array that lets us hold binary data.

They let us manipulate image data, canvas elements, process binary files, etc.

Also, we can use them to interact with native APIs like WebGL for graphics manipulation.

There’s no way to manipulate the binary data from theses APIs without a new data type.

2 kinds of objects work together with Typed Arrays.

They include buffers and views.

Buffers are instances of ArrayBuffer and holds binary data.

Views provide methods for accessing binary data.

There’re 2 kinds of views.

One is an instance of the Typed Array constructor such as Uint8Array , Floar64Array , etc.

These work like abnormal arrays, but only allows one type of elements and don’t have holes.

Another is the instance of DataVBiew that lets us access the byte offsets in the buffer.

Handling Overflow and Underflow

When a value is out of range, modular arithmetic is used to convert it to a value in range.

This means that the highest value plus one is converted to the lowest value.

And the lowest value minus one is converted to the highest value.

This is the case with Typed Arrays.

For instance, if we have:

const uint8 = new Uint8Array(1);
uint8[0] = 255;

uint8[0] is 255.

On the other hand, if we have:

const uint8 = new Uint8Array(1);
uint8[0] = 256;

uint8[0] is 0.

If we have:

const uint8 = new Uint8Array(1);
uint8[0] = -1;

uint8[0] is 255.

This applies to all types of Typed Arrays.

Conclusion

Maps and sets have their limitations. They’re also suitable for some applications.

Typed Arrays are used for storing binary data.

Categories
Modern JavaScript

Best of Modern JavaScript — Typed Arrays and its Uses

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at JavaScript typed arrays usage.

Static Typed Array Properties

Typed arrays have the BYTES_PER_ELEMENT property to count how many bytes are needed to store a single element.

The same property is also available in the typed array’s prototype.

Concatenating Typed Arrays

Typed arrays don’t have a concat method like normal arrays.

We can use the set method with a typed array as the first argument and the offset index as the 2nd argument.

For example, we can write:

const arr = Int8Array.of(1, 2, 3);
const arr2 = Int8Array.of(4, 5, 6);

const arrays = [arr, arr2];

let totalLength = 0;
for (const arr of arrays) {
  totalLength += arr.length;
}

const result = new Int8Array(totalLength);

let offset = 0;
for (const arr of arrays) {
  result.set(arr, offset);
  offset += arr.length;
}

console.log(result);

to create a new result typed array with the length being the total length of all the arrays.

Then we can add each typed array’s entry to the result typed array with the set method.

And we update the offet after each array to add the entries.

DataViews

The DataView constructor lets us create a DataView whose data is stored in a given ArrayBuffer.

Its signature is (buffer, byteOffset, byteLength) .

buffer is the buffer object.

byteOffset is the offset of the buffer in bytes,

byteLength is the length the DataView in bytes.

DataView Instance Properties

DataView has the following instance properties.

The DataView.prototype.buffer is a getter than returns the ArrayBuffer for the DataView.

DataView.prototype.length returns how many bytes can be accessed by the DataView.

DataView.prototype.byteOffset returns the offset of which DataView starts accessing the bytes in its buffer.

DataView.prototype.get returns a value from the buffer of the DataView.

It takes the byte offset to start accessing the array as its first argument.

The 2nd argument is whether to set littleEndian to be true or not.

It’s false by default.

DataView.prototype.set lets us writes a value to the buffer of the DataView.

Its signature is (byteOffset, value, littleEndian=false) , byteOffset is the offset to start writing.

value us the value to write.

littleEndian lets us set the endianness of our DataView.

Typed Arrays Usage

Typed aerays are used in various places.

The File API uses it to store file data ready from a file input.

For example, we can get a typed array by writing:

const fileInput = document.getElementById('fileInput');

fileInput.onchange = () => {
  const file = fileInput.files[0];
  const reader = new FileReader();
  reader.readAsArrayBuffer(file);
  reader.onload = function() {
    const arrayBuffer = reader.result;
    //...
  };
}

We get the file object with fileInput.files[0] .

Then we used the FileReader instance’s readAsArrayBuffer function to read the file’s contents into the array buffer.

reader.result has the result.

Fetch API

The Fetch API also lets us get data as an ArrayBuffer.

For instance, we can write:

fetch('https://file-examples-com.github.io/uploads/2017/10/file-sample_150kB.pdf')
  .then(request => request.arrayBuffer())
  .then(arrayBuffer => {
    console.log(arrayBuffer);
  });

to get a PDF files from a URL and convert it to an ArrayBuffer with the arrayBuffer method on the result.

Other APIs that use typed arrays include canvas and WebSockets.

Conclusion

Typed arrays can be manipulated like arrays.

They’re used in many native browser APIs.

Categories
Modern JavaScript

Best of Modern JavaScript — Typed Array Properties

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at JavaScript typed arrays.

Typed Arrays

Typed arrays are arrays where its elements can only be numbers.

The constructors that can create integer only typed arrays include Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, and Uint32Array .

Floating-point number only arrays include Float32Array and Float64Array .

Typed Arrays vs Normal Arrays

Typed arrays are very similar to normal arrays.

They have a length property and elements can be accessed with the bracket operator with their indexes.

The standard array methods are also available.

All their elements are always of the same type.

Setting elements convert the value to the type allowed.

Typed arrays are always contiguous.

Normal arrays can have holes, but typed arrays can’t.

They’re always initialized with zeroes because they can’t have holes.

For example:

new Array(5)

creates an array with 5 empty slots, but:

new Uint8Array(5)

creates a typed array with 5 zeroes.

A typed array also has an associated buffer.

The elements of a typed array is stored in an associated ArrayByffer that can be accessed with the buffer property.

Typed Arrays are Iterable

Typed arrays are iterable.

It has a Symbol.iterator method to make it an iterable object.

So we can use the for-of loop with them.

For example, we can write:

const arr = Uint8Array.of(1, 2, 3, 4, 5);
for (const byte of arr) {
  console.log(byte);
}

Then we get 1, 2, 3, 4, and 5 logged.

Converting Typed Arrays to and from Normal Arrays

We can convert between typed and normal arrays.

For instance, we can create a typed array from a normal array by writing:

const typedArr = new Uint8Array([1, 2, 3, 4, 5]);

We can convert a typed array to a normal with slice method or the spread operator.

To call slice to convert the typed array to a normal array, we can write:

const typedArr = new Uint8Array([1, 2, 3, 4, 5]);
const arr = Array.prototype.slice.call(typedArr);

We can also use the spread operator by writing:

const typedArr = new Uint8Array([1, 2, 3, 4, 5]);
const arr = [...typedArr];

We can also use the Array.from method to do the same thing.

For example, we can write:

const typedArr = new Uint8Array([1, 2, 3, 4, 5]);
const arr = Array.from(typedArr);

In all 3 examples, we get [1, 2, 3, 4, 5] for arr .

Inheritance Hierarchy of Typed Arrays

Typed array classes all have a common superclass.

The superclass isn’t directly accessible from our JavaScript code.

The prototype property of the typed array constructor holds all the methods of a typed array.

Static Typed Array Methods

Typed array constructors have some static methods.

One of them is the of method.

We can create a typed array from it with the arguments becoming the entries of it.

For example, we can write:

const typedArr = Uint8Array.of(1, 2, 3, 4, 5);

to call of with some numbers which end up in typedArr .

Conclusion

Typed arrays are similar to normal arrays but have many properties that set them apart.

Categories
Modern JavaScript

Best of Modern JavaScript — Typed Array Methods

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at JavaScript typed arrays methods.

Static Methods

The from static method lets us convert an array-like or iterable object into a typed array.

The first argument is the iterable object we want to convert to a typed array.

The 2nd is a callback that has the value of the entry and the index as the parameter of the array to let us return something we want.

The 3rd argument is the value of this we want to have in the callback in the 2nd argument.

The 2nd and 3rd arguments are optional.

For example, we can write:

const typedArr = Uint16Array.from([0, 1, 2]);

We pass in the array value and returns a Uint16Array with the entries from the array.

The callback for mapping the entries let us create a new array that doesn’t overflow rather than have them overflow.

For example, instead of writing:

const arr = Int8Array.of(200, 201, 202).map(x => 2 * x)

And get the Int8Array with values [-112, -110, -108] . We can write:

const arr = Int32Array.from(Int8Array.of(120, 121, 122), x => x * 2)

and get [240, 242, 244] in an Int32Array .

Instance Properties

There are many instance properties included with typed arrays.

The buffer property is a getter than returns the buffer that’s storing the data of the typed array.

The byteLength property is a number that returns the size in bytes of the typed array’s buffer.

byteOffset returns the offset where the typed array starts inside the ArrayByffr.

set copies all elements of an array or typed array into the given typed array.

If it’s a normal array, then all elements are converted to numbers.

If the argument is a typed array, then each element is converted to the type appropriate for the typed array.

subarray returns a new typed array that has the same buffer as the typed array it’s called on.

It takes the beginning index as the first argument. The default value of this is 0.

The end index is its second argument. The default value of this is the length of the array.

The values can be negative.

Array Methods

Array methods like copyWithin , entries , every , fill , filter , find , findIndex , forEach , indexOf , join , keys , lastIndexOf , length , map , reduce , reduceRight , reverse , slice , some , sort , toLocaleString , toString , and values are all available to typed arrays.

They take the same arguments and have the same return values as regular array methods.

Constructor

Typed arrays constructors include Int8Array, Uint8Array, Uint8ClampedArray , Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array.

They can have the signature (buffer, byteOffset=0, length?) , (length) , (typedArray) ,(arrayLikeObject) , or () .

buffer is the buffer of a typed array.

ByteOffset is the offset from index 0.

length is the length of the typed array.

typedArray is another typed array.

arrayLikeObject is an array-like or iterable object.

An array-like object is one with the length property and integer keys.

Conclusion

Typed arrays have many array methods.

The constructor is different from the regular Array constructor.

Categories
Modern JavaScript

Best of Modern JavaScript — Sets and WeakSets

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at sets and WeakSets.

Set API

The Set API has various methods we can use to let us manipulate it.

The constructor lets us pass in an iterable object and create a set from it.

For instance, we can write:

const set = new Set([1, 2, 3]);

to create a set from an array.

The Set.prototype.add method takes a value and appends it to the set.

It returns the set with the new entry.

Set.prototype.has takes a value and returns true if it’s in the set and false otherwise.

The Set.prototype.delete method takes a value and lets us remove it.

It returns true if it’s deleted and false otherwise.

The Set.prototype.size getter returns the number of items in the set.

The Set.prototype.clear method lets us remove all items from a set.

Set.prototype.values is a method that returns all values of a set as an iterator.

Set.prototype[Symbol.iterator] returns an iterable object that lets us iterate over a set.

The Set.prototype.forEach let takes a callback with the signature, (value, key, collection) as the signature, and runs it for each item in the set,

value has the set value.

key has the same value as value .

The collection is the set itself.

The 2nd argument is the value of this we use in the callback.

Sets also have the Set.prototype.entries method to return an iterable object with each entry being a key-value array.

The key and value are the same.

The Set.prototype.keys method returns us an iterable object with the keys, which is the same as the values.

WeakSet

A WeakSet is a set that doesn’t prevent its elements from being garbage collected.

It works like WeakMaps and doesn’t allow iteration, looping, or clearing.

There isn’t many uses cases for WeakSets.

We can add objects to them and then get them with the reference.

So we can write:

const weakSet = new WeakSet();

const obj = {};
weakSet.add(obj);
const hasObj = weakSet.has(obj);

We pass in a map and then check for its value with has .

We can only pass objects into a WeakSet.

We’ll get a TypeError if we try to pass in a primitive value.

Also, we can use WeakSets to allow us to run a method on a given class instance.

For example, we can write:

const bars = new WeakSet();

class Bar {
  constructor() {
    bars.add(this);
  }

  method() {
    if (!bars.has(this)) {
      throw new TypeError('not a bar instance');
    }
  }
}

We add the Bar instance to the bars WeakSet in the constructor.

Then in the method method, we check if the Bar instance if part of the WeakSet with has .

If it’s not, then we throw an error.

This prevents the method from being run on anything other than Bar instances.

WeakSet API

WeakSets has a simple API with 3 methods and works like their Set equivalents.

WeakSet.prototype.add takes a value as an argument and lets us add an entry to it.

WeakSet.prototype.has takes a value and returns true if the entry exists and false otherwise.

WeakSet.prototype.delete takes a value and removes the element from the WeakSet.

Conclusion

Sets let us add items without duplicating.

WeakSets let us hold items that can be garbage collected when they aren’t used.