Collections
Have several native data structures for advanced usages.
Mainly use Array / Object to create custom or complicated data structures
Before ES6
After ES6
- TypedArrays
- Maps & WeakMaps
- Sets & WeakSets
-
TypedArray is not one of the object type. It's a collection of a bunch of objects.
-
In JS, array's not the same as traditional languages like C or Java. It's not a consecutive space in memory and accessed by address offset. If we want to improve performance, we could use ArrayBuffer, which is more like using malloc in C. The OS will allocate a consecutive memory space.
-
Can't control buffer(ArrayBuffer) directly. Must access it with view (TypedArray/DataView). Example will be shown later.
- Metaphorically speaking, it's like you have to register the type of land (agriculture/industrial/residence) before using.
TypedArrays
There are 9 types
TypedArrays
- Int8Array
- Uint8Array
- Uint8ClampedArray
- Int16Array
- Uint16Array
- Int32Array
- Uint32Array
- Float32Array
- Float64Array
TypedArrays
let buf = new ArrayBuffer(32)
buf.byteLength // declare a space with 32 byte (256 bits)
let arr = new Uint16Array(buf)
arr.length; // use this buffer to store Uint16 data, 256 ÷ 16 = 16 slots
Endiannes(Bytes Order)
When CPU access multiple bytes, there are two kinds of rules.
- Little-endian: Start with less significant byte, also called LSB.
- Big-endian: Start with most significant byte, also called MSB.
Endiannes(Bytes Order)
let buf = new ArrayBuffer(2)
let view8 = new Uint8Array(buf)
let view16 = new Uint16Array(buf)
view16[0] = 3085; // The binary representation is 0000110000001101 for integer 3085
view8[0]; // Most CPU use LSB, therefore, the binary representation for view8[0] is 00001101 (13 in decimal)
view8[1]; // The binary representation for view8[1] is 00001100 (12 in decimal)
view8[0].toString(16); // "d" in hex
view8[1].toString(16); // "c" in hex
// swap (as if endian!)
let tmp = view8[0];
view8[0] = view8[1];
view8[1] = tmp;
view16[0]; // The binary represnentation becomes 0000110100001100 (3340 in decimal)
Note: The same buffer could be controlled by many Views. If one view changes the buffer, the result will be reflected when using other views!
-
Share most methods in Array.prototype, including map, forEach, reduce, filter...
- For the methods that would alter the size/length, for example, push, unshift, splice..., they could not be used.
TypedArrays - Prototype
let a = new Uint32Array(3)
a[0] = 10;
a[1] = 20;
a[2] = 30;
let b = a.map(ele => ele * 2)
b.forEach(ele => console.log(ele)) // 20 40 60
b.push(100) // Uncaught TypeError: b.push is not a function
Since the number of bits are fixed, it could lead to an overflow!
TypedArrays - Overflow
let a = new Uint8Array(3)
a[0] = 10;
a[1] = 20;
a[2] = 30;
let b = a.map(ele => ele * ele)
b.forEach(ele => console.log(ele)) // 100 144 132
The biggest UInt for 1 byte (8 bits) could store is 255. The second and third slot (400 & 900) has an overflow.
TypedArray also have the method .from. It could receive an Iterable and return a whole new TypedArray/Array.
TypedArrays - Overflow
let a = new Uint8Array(3)
a[0] = 10;
a[1] = 20;
a[2] = 30;
let b = Uint16Array.from(a, ele => ele * ele)
console.log(b) // Uint16Array(3) [100, 400, 900]
Array sorts in alphabetical order, whereas TypedArray sorts in numeric order.
Sort Between TypedArrays & Arrays
let a = [1,2,3,10,11]
let b = new UInt8Array(a)
a.sort() // [1, 10, 11, 2, 3]
b.sort() // [1, 2, 3, 10, 11]
When using objects, if keys are also objects, they will be overwritten. All keys will be stringified into "[object Object]".
Maps
let obj = {}
let x = { id: 1 }
let y = { id: 2 }
obj[x] = 'foo'
obj[y] = 'bar'
console.log(obj[x]) // bar
console.log(obj[y]) // bar
We could solve this issue by using Map. Anything could be used as a key without being stringified.
Maps
let m = new Map()
let x = { id: 1 }
let y = { id: 2 }
m.set(x, 'foo')
m.set(y, 'bar')
m.get(x) // foo
m.get(y) // bar
m.size // 2
m[x] // Can't use [] (bracket notation), could only use .set / .get
m.has(x) // true
m.delete(x) // can't use the keyword delete, must use .delete method in Map
m.has(x) // false
m.size // 1
m.clear() // clear all key/value pairs in Map
We could also pass a 2d array to construct a map. The first element of every subarray will be used as the key, while the second element will be used as the value. Elements after the second one will not be used.
Maps
let m = new Map([
['a', [1,2,3]],
['b', [4,5,6]]
])
m.get('a') // [1,2,3]
m.get('b') // [4,5,6]
We could use .values to extract the values of the Map, however, the return value will be an iterator. We could use ...(spread operator) or for-of to convert it into an array. (The same as .keys)
Maps - Get Values
let m = new Map([
['a', 10]
['b', 20]
])
let vals = [...m.values()] // [10, 20]
Array.from(m.values()) // Could also use Array.from
for (let val of map.values()) {
console.log(val)
}
Like sets in math. Only hold unique values.
Sets
let s = new Set()
var x = { id: 1 },
y = { id: 2 }
s.add( x )
s.add( y )
s.add( x )
s.size // 2
s.delete( y )
s.size // 1
s.clear()
s.size
Sets
let s = new Set()
var x = { id: 1 },
y = { id: 2 },
z = { id: 1}
s.add(x)
s.add(y)
s.add(z)
s.size // 3 -> although x and z look identical in content, they point to different objects with their own memory space
let w = z
s.add(w)
s.size // 3 -> w & z points to the same space (same space in memory)
Sets
let s = new Set()
var x = { id: 1 },
y = { id: 2 },
z = { id: 3}
s.add(x).add(y).add(z)
[...s.keys()] // { id: 1 } { id: 2 } { id: 3 }
[...s.values()] // { id: 1 } { id: 2 } { id: 3 }
[...s.entries()]
// [{ id: 1 },{ id: 1 }] [{ id: 2 },{ id: 2 }] [{ id: 3 },{ id: 3 }]
ES6 Collections
By ianyshuang
ES6 Collections
- 154