JavaScript: The Fun Parts
Miklos Bertalan
Posted 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 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`)
}
}
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
}
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()
}
})
}
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: () => true
})
It tweaks with
behavior with the following tricks:
It uses
new Function()
- instead ofeval
- to create functions in the global scope. The global scope is outside strict mode sowith
works there.It uses Proxies to tweak
with
behavior a bit. Normallywith
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 trickwith
into thinking that every possible property is present on the passed object. This is pretty simple with thehas
Proxy trap.
Everything Together
We accidentally created a blazing fast lightweight MVC framework along the way.
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!
Posted on February 12, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
January 15, 2023