Parameter destructuring

The benefits of using ES6 parameter destructuring over options object pattern.

I have been using parameter destructuring when making function calls and I love it. I use the destructuring where I used to call functions with an options object. I find the new way much simpler, safer and easier to read than options object.

The options object

Any function with more than 2 arguments can benefit from using an options object pattern to pass its arguments.

1
2
3
4
5
6
// BAD
function doSomething(a, b, c, d) { ... }
// GOOD
function doSomething(options) {
// use options.a, options.b, options.c, ...
}

When passing an options object, I no longer need to worry about or remember the function's signature; or how to pass the default values for parameters I want to skip.

Parameter destructuring

The ES6 allows me to construct and destruct an object on the fly. For example, I can still call the function with an object, but inside the function grab only the needed properties.

1
2
3
4
5
6
7
8
9
function doSomething({a, b}) {
// use a and b variables
}
var o = {
a: ...,
b: ...,
c: ...
}
doSomething(o)

The destructuring has several benefits in my opinion.

The input values are immutable

Nothing (except deep freezing) prevents a function from modifying properties inside a passed object. This affects any code that passes around options object.

1
2
3
4
5
6
7
8
9
10
function doSomething(options) {
options.c = -1 // EVIL!
}
var o = {
a: ...,
b: ...,
c: 1
}
doSomething(o)
// o.c = -1 now

Destructuring on the other hand severely limits what the function can do, because the inputs are passed by value. Even better, the function does NOT have access to the inputs NOT listed in its signature.

1
2
3
4
5
6
7
8
9
10
11
12
function doSomething({a, b}) {
// cannot access "c"
a = 100
}
var o = {
a: 0,
b: ...,
c: 1
}
doSomething(o)
// o.a = 0 // good - "a" is unchanged
// o.c = 1 // good - "c" is unchanged

The later approach makes the code easier to read and understand.

Destructuring makes it easy to use consistent variable names

When passing individual variables or options object, it is easy to make up variable names. But if we apply destructuring when calling the function, we have to use the same variable names at the caller as inside the function (I am ignoring parameter renaming feature, since it just adds more noise to the code in my opinion).

1
2
3
4
5
6
7
8
9
10
11
// BAD
function compare(options) {
return options.apples > options.oranges
}
var apps = 2,
ors = 10
var options = {
apples: apps,
oranges: ors
}
compare(options)

Note that we used variables apps and ors in the caller, and had to use the expected property names only when calling the compare function. We could not even easily inspect the signature of compare because options parameter is opaque.

In contrast, if we create an object from variables, we need to have the variables match names with the declared parameter names in the function signature, keeping the whole thing consistent.

1
2
3
4
5
6
7
8
9
// GOOD
function compare({apples, oranges}) {
return apples > oranges
}
// have to use names because preparing for
// object creation
var apples = 2,
oranges = 10
compare({apples, oranges})

The above syntax is short and consistent. I like the fact that the caller is "forced" to use the parameter names from the function's public "API" (the function's signature).

Partial application

One downside of the options and destructuring technique is that one cannot partially apply parameter values easily.

1
2
3
4
5
6
7
// "normal" function
function add(a, b) { return a + b }
const increment = add.bind(null, 1)
increment(4) // 5
// destructuring function
function compare({apples, oranges}) { return apples > oranges }
// ?

If we know the value of "apples" or "oranges" already, how do we create a new function that just waits for the remaining value? Luckily, a function to do this is simple to write, and the one I wrote for working with options object applies perfectly to destructured calls. obind stands for "options bind" and can "pre-fill" properties inside a given object.

1
2
3
4
5
const obind = require('obind')
function compare({apples, oranges}) { return apples > oranges }
const compareTo10apples = obind(compare, {apples: 10})
var oranges = ...
compareTo10apples({oranges})

Nice!