My New Favorite ES6 Toy: Destructured Objects as Parameters

Avatar of Sarah Drasner
Sarah Drasner on

Like a lot of other developers, I’m working through my continued education learning what I can about ES6. One of the ways I’m doing this is to attend workshops by smart people. I went to Kyle Simpson’s ES6: The Good Parts course and found myself particularly interested in the practical applications of a piece of ES6 I had previously not noticed: Destructured Objects as Parameters.

Some background

As an application grows our function parameters can become bloated. This is a problem for a number of reasons:

  • We have to remember the specific ordering of these parameters when we call the function. If that’s more than three, it takes some context-shifting to compare.
  • We don’t have defaults out of the box
  • To properly self-document, we rely on naming very heavily. Naming arguments in code reviews are so fun!

How do we solve this with ES6?

Thinking very broadly about passing objects as parameters instead might seem at first to be pretty straightforward, and maybe even slightly more convoluted as people aren’t used to reading parameters and thinking of them this way. But look at all the issues from above that this solves:

  • You no longer have to remember the specific ordering
  • We can easily have optional parameters
  • We can have defaults, which is also nice because it’s self-documenting
  • We can have fallbacks for failure conditions, or decide not to if we want to show an error

Destructuring can help us easily extract and manipulate data from arrays or objects into distinct variables and make our parameters more meaningful. This helps us communicate our intentions to readers in the future and helps with maintenance.

What is destructuring?

Destructured objects as parameters combines a few things into one. Let’s start with the concept of destructuring as a whole before showing this particular application. There are a lot of uses and applications for destructuring, perhaps most commonly applied when you have a large JSON object or API and you want to extract meaningful variables from them. Destructuring is possible in ES5, but due to a combination of a lot of features in ES6, destructuring is a lot more legible and valuable.

We’re only going to be speaking about destructuring objects (not arrays), but it’s important to note that both are possible. We’ll start with a very basic and slim comparison of object destructuring in ES5 vs ES6:

Here is how we destructure an object in ES5:

var o = {a: 1, b: 2, c: 3};
  
var chico = o.a,
    harpo = o.b,
    groucho = o.c;

console.log(chico, harpo, groucho);
// 1 2 3

And now, its ES6 counterpart:

const o = {chico: 1, harpo: 2, groucho: 3};

const { chico, harpo, groucho } = o;

console.log(chico, harpo, groucho);
// 1 2 3

This has some really useful practical applications. We can create top-level variables from objects, a few properties at a time, and even extract them when they are nested a few deep.

const o = {
  chico: 1, 
  harpo: 2, 
  groucho: 3,
  othermarx : {
    zeppo: 4,
    isthatamarxbrother: {
      gummo: 5,
      polly: 6
    }
  }
};

const { gummo, polly } = o.othermarx.isthatamarxbrother;

console.log(gummo, polly);

We can even rename them in the process, by doing something like const { gummo, polly:cousin } = o.othermarx.isthatamarxbrother;

This article will not go over all of destructuring so that we can focus on one practical application. If you’re interested in learning more about it, how it applies to arrays in ES6, and some other nice uses, here are some good resources:

How do we use this in a function?

Let’s start with a really bare-bones example of a function with some parameters. Let’s say we need to have a manifest with a lot of standard shipment data. This data stays the same, for the most part, but has some deviation from day to day. Defaults make a lot of sense here, but we would have to write out something like this:

function shipmentES5Defaults(params) {
  params = params === undefined ? {} : params;
  var items = params.items === undefined ? 'bananas' : params.items;
  var number = params.number === undefined ? 5: params.number;
  var pkg = params.pkg === undefined ? 'crates' : params.pkg;
  console.log("We have a shipment of " + items + " in " + number + " " + pkg + ".");
}

shipmentES5Defaults();
// We have a shipment of bananas in 5 crates.

shipmentES5Defaults({
  items: 'cherries',
  pkg: 'bags'
});
// We have a shipment of cherries in 5 bags.

This is nice, because we have our defaults and don’t return an undefined if something isn’t specified.

But in ES6, we can use the destructured object we just covered as our parameters, allowing us a quick and easy way to provide defaults. Aside from being useful, it’s a bit self-documenting as well. This is pretty helpful and a really nice way to write our functions with legibility in mind. Not to mention that the ES6 template literal makes the string really easy to both read and write.

function shipmentES6({ items = 'bananas', number = 5, package = 'boxes' } = {}) {
  console.log(`We have a shipment of ${items} in ${number} ${package}.`);
};

shipmentES6({ package: 'crates' });
// -> We have a shipment of bananas in 5 crates.
shipmentES6({ items: 'tomatoes', number: 18 });
// -> We have a shipment of tomatoes in 18 boxes.
shipmentES6();
// -> We have a shipment of bananas in 5 boxes.

Aside from all of these benefits, if you and your coworkers or collaborators get used to writing in this style, then if you don’t pass a default, you are also communicating that there are no defaults and you’d rather it error out.

Stay tuned for our next post, where we’ll use this way of writing parameters to construct a SVG chart with no external libraries.