Exotic JS Features in Practical Examples

JavaScript is evolving rapidly with a bunch of yearly additions. Some of them never reach the spotlight, others get forgotten or deprecated. Let's put these into increasingly hacky editable code sandboxes. The goal is to make you play with occasional horror on your face.

The examples are taken from real libraries or apps that I have written over the years. The last few are controversial though, and I do not encourage you to use them in your code.

Finally

I usually use finally when I have to wrap other devs' functions with some extra logic. finally does this without messing with the original behavior - like return values or thrown Errors.

This example instruments the passed function with performance measurements.

function measure (fn) {
  const start = Date.now()
  try {
    return fn()
  } finally {
    console.log(`${fn.name} took ${Date.now() - start} ms`)
  }
}

Edit l9wj5zx20l

WeakSet and WeakMap

These two provide truly private properties (unlike Symbols). I use them when I have to append some metadata to others' objects. Directly mutating these would be bad manners, plus you get screwed if the object is frozen.

This example stringifies the passed object and caches the result. The memoization speeds up the operation and the WeakMap usage avoids object pollution and memory leaks.

const cache = new WeakMap()

export default function stringify(obj) {
  let result = cache.get(obj)
  if (!result) {
    result = JSON.stringify(obj, null, 2)
    cache.set(obj, result)
  }
  return result
}

Edit wn1ror8jz5

Proxy

Proxies can tweak language behavior. Most of the time I use them to extend the language and occasionally I use them completely change some default behavior.

This reactive example lets you create observables and reactions. A reaction automatically re-runs when an observable - used inside it - is mutated. The snippet is extremely oversimplified but some additional coding can take it pretty far.

let runningFn
const reactions = new WeakMap()

export function observe(fn) {
  runningFn = fn
  try {
    return fn()
  } finally {
    runningFn = undefined
  }
}

export function observable(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      runningFn && reactions.set(target, runningFn)
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver)
      const reaction = reactions.get(target)
      reaction && reaction()
    }
  })
}

Edit wn7nz5x7x8

New Function

new Function() is similar to eval but it creates functions in the global scope, which is outside of any 'strict mode' declarations. We will abuse this fact soon.

This example gets the global object - regardless of the platform - which is currently trickier than it should be. It is also used in the ES2019 globalThis proposal polyfill.

const globalThis = new Function('return this')()

This won't work if you have CSP set up for your page.

With

with extends the scope chain for a statement. Sadly, it is deprecated and it won't work in strict mode - which includes ES6 modules. A few tricks can save the day though.

This example lets you write Vue or Angular like template files and renders them in the context of the passed state.

export default function compile(template) {
  return new Function("state", `with (wrap(state)) { return \`${template}\` }`)
}

window.wrap = state => new Proxy(state, {
  has() { return true }
})

Edit wyv631ry7k

It tweaks with behavior with the following tricks:

  • It uses new Function() - instead of eval - to create functions in the global scope. The global scope is outside strict mode so with works there.

  • It uses Proxies to tweak with behavior a bit. Normally with extends the scope chain and does not replace it completely. When a property is not found on the passed object it will search for it in the next scope - in our case the global one. We do not want the global scope to leak into the template so we have to trick with into thinking that every possible property is present on the passed object. This is pretty simple with the has Proxy trap.

Everything Together

We accidentally created a blazing fast lightweight MVC framework along the way.

Edit 72oynq44jq

Please check the repo here and don't forget to leave a star!


I hope you found some time to play with the sandboxes. Leave a comment if you created something crazy.

Thanks for reading!

Learn Something New Everyday,
Connect With The Best Developers!

Sign Up Now!

& 500k+ others use Hashnode actively.

No Comments Yet