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'... [Read Full]
markdown guide
 

How about empty loops, they're a neat but slightly unknown feature:

var total = 0;
for (var i = 0; i < myarr.length; total += myarr[i++]);

This isn't a perfect example (could use .reduce) but it gives you an idea of how tidy looking empty for loops can be.

Also, in a for..in loop the expression before in can be any left-hand side of an assignment.

var obj = {'foo': 'bar', 'baz': 'quux'},
    arr = [],
    i = 0;
for (arr[i++] in obj);
// arr is now ["foo", "baz"]
 

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
      }
}

 

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.)

 

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 :/

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

 

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 :)

code of conduct - report abuse