js
JavaScript, JS, twerkScript, ECMAscript, call it whatever you want. Here are some neat party tricks you can perform at (including, but not limited to) birthdays, job interviews and cat cafes.
Function identifying with ES6 symbols
Checking if functions / objects are equal in JS is typically done with the
===
operator. This checks if both functions point to the same location in
memory, and then returns a Boolean. In cases where you want to check if a
function or object is of a certain type (e.g. generated by a factory), you're
going to have a hard time.
What you would want to do is attach a flag to the object to mark it as being a certain type. Luckily with ES6 you can attach unique, enumerable flags (which means they don't show up when iterating over the keys) by using ES6 symbols.
Here's an example:
const sym = Symbol('my unique string')
function generate () {
const obj = {foo: 'bar'}
obj[sym] = true
return obj
}
const nw = generate()
const ot = generate()
nw === ot // => false
nw[sym] // => true
// don't ever do this, as if both values
// are undefined, it will also return true
nw[sym] === ot[sym]
Only execute function if it exists
Cute little trick to only execute functions if they exist. Removes the need for noop functions.
function myFunc (fn) {
fn && fn()
}
Common module signatures
In order to form plug-and-play systems with swappable components it is key that
module signatures remain the same between modules. In statically typed languages
it is possible to statically define the signatures for the modules, but this
doesn't work for js. In order to scratch that itch, the level community wrote
abstract-leveldown
: a set of tests that can be used by implementers to enforce
an interface. So far it seems to succeed in it's goal, spreading to other
projects. Known projects to use this pattern are:
Sync-or-async
Mocha has a neat little pattern that turns a function either sync or async
based on if the function expects a callback or not. The trick to doing this is
in using Function.length
. Here's an example implementation of the pattern
mocha uses:
// fn, fn -> null
function detect (fn, cb) {
if (fn.length) return fn(() => cb())
fn()
cb()
}
cb
is the callback that is called when donefn
is the main function that we're callingfn.length
checks iffn
expects an argument, and then passes a callback if it doesif
fn
expects no arguments, we just call fn andcb
Find file in root of project
const root = path.dirname(require.main.filename)
const localPackage = require(path.resolve(root + '/package.json'))
Transducers
Transduction: the action or process of converting something and especially energy or a message into another form
Transducers appear to be all the hype, providing faster map
functions than are
included in JS by default. But how do they work? Easy!
const arr = [1, 2, 3, 4]
// this code loops over
// the array twice
arr.map(val => val + 1).map(val => val % 3)
// this code loops over
// the array once thanks to
// transdusuper powers!
arr.map(val => {
val = val + 1
val = val % 3
return val
})
Transducer libraries provide a set of predefined set of composable functions, but you just as easily create your own.
In terms of performance we move from On^maps
to On
. This means that for
n<=1
, no speed is gained. So keep in mind that if you're using transducers
for large data sets they only reduce the amount of passes, but not the speed
of the operations.
Prototypes
Prototypes are a tricky beast; they work differently than classes and can be tough to reason about. Luckily there's a simple heuristic to remember how they work: they're simply a linked list. Multiple inheritance is just multiple pointers in the parent field.
delete value from array
const arr = [2, 5, 9]
const i = arr.indexOf(9)
if (i > -1) arr.splice(i, 1)
Create a new promise
const prom = new Promise((resolve, reject) => {
resolve()
})
Testing CLI applications
Either through exec
or pipe
.
exec
const exec = require('child_process').exec
const fs = require('fs')
const cmd = 'echo ./* | ' + Bulk.cmd+' -c "pwd"'
var dir = __dirname + '/node_modules/@scoped'
exec(cmd, {cwd: dir}, function(err, stdout, stderr) {
var dirs = fs.readdirSync(dir).map(function(item) {
return path.join(dir, item)
})
t.ifError(err)
t.deepEqual(stdout.trim().split(/\s+/g), dirs)
t.end()
})
pipe
const bulk = Bulk('pwd')
const bl = require('bl')
bulk.stdout
.pipe(bl(function(err, chunk) {
var stdout = chunk.toString()
t.ifError(err)
var expected = fs.readdirSync(dir).map(function(item) {
return path.join(dir, item)
})
t.deepEqual(stdout.trim().split(/\s+/), expected)
t.end()
}))
bulk.stdin.end(dirs.join(' '))
Short indexof
Perform a bitwise flip on -1
for truthy check:
if (~['hi'].indexOf('foo')) // do something
Loop through alphabet
const alphabet = []
var n = 0
while (n++ < 26) {
var code = 'a'.charCodeAt(0) + n)
var str = String.fromCharCode(code)
alphabet.push(str)
}
Async
- APIs should be consistent
- if async always be async or risk unleasing zalgo
use
dezalgo
http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony
pretty print json
// indentation of 2
JSON.stringify({ foo: 'bar' }, null, 2)
cast time to milliseconds
new Date('2011-01-26T19:06:43Z').valueOf()
> 1296068803000
Capitalize first letter
function capitalize (text) {
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
}
Get year month day
(new Date()).getFullYear() // year
(new Date()).getMonth() // month
(new Date()).getDate() // day of month
(new Date()).day() // day of week (number)
Get name of thing
<obj>.constructor.name
See Also
- ES6 compat table - caniuse for js
- js pitfalls
- crosswalk project - native app runtime