DEV Community

Cover image for JavaScript: The Fun Parts

JavaScript: The Fun Parts

Miklos Bertalan on February 12, 2019

JavaScript is evolving rapidly with a bunch of yearly additions. Some of them never reach the spotlight, others get forgotten or deprecated. Let's ...
Collapse
 
qm3ster profile image
Mihail Malo • Edited

Looking for advice, do you feel like using a WeakMap would be better here than using the symbol?

import { BufferWithPointer } from "./buffer"

const byteLength = Symbol("ByteLengthRead")

/** Utility to specifiy that a reader function consumes a constant length of buffer */
export const fixedLength = <
  L extends number,
  F extends ((r: BufferWithPointer) => any) & { [byteLength]?: L }
>(
  len: L,
  fn: F
) => {
  if (!Number.isInteger(len) || len < 1)
    throw new TypeError(
      `byte length must be a positive integer, got ${len} instead`
    )
  fn[byteLength] = len
  return fn as F & { [byteLength]: L }
}

/** Wraps a reader function, to read until end of buffer and return array */
export const readUntilEnd = <R>(
  fn: ((r: BufferWithPointer) => R) & { [byteLength]?: number }
): ((r: BufferWithPointer) => R[]) => {
  const len = fn[byteLength]
  return len
    ? r => {
        const repeats = r.remaining() / len
        if (!Number.isInteger(repeats))
          throw new RangeError(
            `Bad buffer: Remaining length not multiple of repeat unit length`
          )
        const arr: R[] = new Array(repeats)
        for (let i = 0; i < repeats; i++) {
          arr[i] = fn(r)
        }
        return arr
      }
    : r => {
        const arr: R[] = []
        let last = r.remaining()
        while (last) {
          arr.push(fn(r))
          const remaining = r.remaining()
          if (remaining >= last)
            throw new Error(`Infinite loop: repeat unit isn't consuming buffer`)
          last = remaining
        }
        return arr
      }
}

Collapse
 
solkimicreb profile image
Miklos Bertalan • Edited

Yep, I think WeakMap suits this more but it's just a semantical change in this case, not a critical one. Theoretically, this implementation could cause issues with some Proxy based libraries (which do intercept Symbol get/set).

Generally, I like to use symbols like well-known symbols. When I create an internal feature that I want to expose to experienced devs (by exporting the symbol) but don't want to be accidentally overwritten (like a normal prop).

I hope this helps a bit.

PS: I use both Symbols and WeakMaps in a lib which I perf profile often and I found no real difference in performance (in Chrome and Node at least.)

Collapse
 
qm3ster profile image
Mihail Malo

Cool. Maybe I'll switch then.
It was fun to type this, with the "wrapped" functions having a literal number attached to them, but it's not like either the Symbol or the WeakMap will ever be exported.

On the other hand, I won't be able to tell if a function is "wrapped" by type tooltip any more or see its bytelength :/

Thread Thread
 
solkimicreb profile image
Miklos Bertalan

That's true. I only use TypeScript when I must so I tend to not think about typings.

Collapse
 
ma5ly profile image
Thomas Pikauli

Great article! A lot of these I didn't know. I especially like finally, from the naming it already says a lot about what it does. Your measuring example is nice too, I might use that myself :)