Destructuring, The Spread Operator, And Related Neat Stuff

(Pssst! All the code for this post can be found at https://github.com/NerdcoreSteve/destructuring-the-spread-operator-and-other-neat-stuff)

Hey, check this out:

const thing = "This is a thing"

const someObject = { thing }

console.log(someObject)  
// prints { thing: 'This is a thing' }

That's a new JavaScript feature called object shorthand. It avoids having to do repetitive crap like this:

const someOtherObject = { thing: thing }  
// Why bother when the alternative is so much cleaner?

console.log(someOtherObject)  
// prints { thing: 'This is a thing' }

It can be pretty helpful when returning values from functions:

const somefunc = () => {  
    const something = "this sure is something"
    return { something }
}

console.log(somefunc())  
// prints { something: 'this sure is something' }

Pretty cool right? Here's another trick:

const anotherFunc =  
    ({name, smell}) =>
        `Hey ${name} you smell like ${smell}`

console.log(  
    anotherFunc(
        {smell: 'pine cones', name: 'Homestar'}))
// prints Hey Homestar you smell like pine cones

This is called destructuring. If, in the argument list of a function, you want to grab some sub-parts of that object and assign them to new variables to be used in that function, this is how you do it.

And you don't have to use the key-names of the given object:

const weirdAdd = ({a: x, b: y}) => x + y  
console.log(weirdAdd({a: 2, b: 3}))  
//prints 5

Destructuring also works outside function definitions:

const spiderman = {  
    name: 'Spider-Man',
    alterEgo: 'Peter Parker',
    powers: [
        'wall crawling',
        'spider sense',
        'spider agility',
        'enhanced strength'
    ]
}

const { powers: spiderPowers } = spiderman  
console.log(spiderPowers)  
/*
prints  
[ 'wall crawling',
  'spider sense',
  'spider agility',
  'enhanced strength' ]
*/

Say, what if I just wanted to print each element of the list, not the list itself? After all, console.log will take any number of arguments and print them:

console.log(1, 2, 3)  
//prints 1 2 3

Check it out:

console.log(...spiderPowers)  
// prints wall crawling spider sense spider agility enhanced strength

Those three dots I put in front of spiderPowers? That's the spread operator. It spreads stuff out. Here we're spreading an entire array into a list of parameters sent to a function.

Check this out:

const tail = ([head, ...tail]) => tail  
const head = ([head, ...tail]) => head

console.log(...tail(spiderPowers))  
//prints spider sense spider agility enhanced strength

console.log(head(spiderPowers))  
//prints wall crawling

Of course we can do all this with apply and slice but why mess around with that crap when you have something this nice to work with?

If you're using JavaScript beyond es2015, the spread operator even works on objects:

console.log({...spiderman, superHeroFriends: ['Johnny Storm', 'Black Cat', 'Daredevil']})  
/*
prints  
{ name: 'Spider-Man',
  alterEgo: 'Peter Parker',
  powers:
   [ 'wall crawling',
     'spider sense',
     'spider agility',
     'enhanced strength' ],
  superHeroFriends: [ 'Johnny Storm', 'Black Cat', 'Daredevil' ] }
*/

This is a nice way to extend objects. Without it you'll have to resort to building your own objects manually, using Object.assign or Ramda lenses.

I still find lenses useful but I no longer need them for a simple case like this.

Here's another trick:

const madLib =  
    ({
        subject = 'I',
        pastTenseVerb = 'drank',
        owner = 'their',
        object = 'milk shake'
     } = {}) =>
        `${subject} ${pastTenseVerb} ${owner} ${object}!`

console.log(madLib())  
//prints I drank their milk shake!

console.log(madLib({}))  
//prints I drank their milk shake!

console.log(madLib({subject: 'you', object: 'apple cider'}))  
//prints you drank their apple cider!

console.log(madLib({  
    subject: 'Batman',
    pastTenseVerb: 'massaged',
    owner: 'superman\'s',
    object: 'shoulders'}))
//prints Batman massaged superman's shoulders!

madLib is a function that takes an object as it's sole argument. The default value for that object is an empty object. The default key-values for that object are subject = 'I', pastTenseVerb = 'drank', owner = 'their', object = 'milk shake' respectively.

The nesting for destructuring can go as deep as you like:

const someFunc =  
    ([first, second, third, {name, address: {streetName, number} }, ...remainder]) =>
        `The 4th person in the list is ${name}. They live on ${number} ${streetName}`

console.log(someFunc(people))  
//prints The 4th person in the list is Bob. They live on 1234 First st

I wanna wrap up with a neat trick I got from Eric Elliot's twitter:

const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);  
const newFunc = pipe(fn1, fn2, fn3);  
const result = newFunc(arg);  

Just like my old friend R.pipe, pipe returns a function that takes a single argument. It takes list of functions using the spread operator. That is really nifty.

This isn't an exhaustive list of destructuring and similar tricks, just of the ones I've found useful so far. You can read more about this stuff on MDN.

Looking for a software developer?