DEV Community

Per Enström
Per Enström

Posted on • Updated on

Four tricky JavaScript concepts in one line of code

My colleague approached me the other day with a line of JavaScript code he had found on Stack Overflow, and asked how it worked. And I thought it was such a good example of four mid to advanced concepts in JavaScript so I decided to write down my explanation here as well.

The line in question is this

const result = (({ a, c }) => 
  ({ a, c }))({ a: 1, b: 2, c: 3, d: 4 });
Enter fullscreen mode Exit fullscreen mode

Before reading on, give it a think and see if you can work it out by yourself.

Ready to go on? Let’s go.

Object destructuring

Documentation at MDN

Object destructuring is the concept of picking properties from an object in batch instead of manually accessing each property and assigning them to a variable. Say you have an object coming in as a parameter in some function, and you want to do stuff with only a few of the properties of that object. Object destructuring makes that possible.

Instead of doing

const a = myObject.a;
const b = myObject.b;

doStuff(a, b);
Enter fullscreen mode Exit fullscreen mode

we can shorten it to

const { a, b } = myObject;

doStuff(a, b);
Enter fullscreen mode Exit fullscreen mode

which does the same thing. This makes the code much smaller, especially when we do stuff to multiple properties of an object. This saves us writing the full property path every time.

The fancy stuff here is that this sort of destructuring works anywhere we have an object. Even when assigning input parameters for a function. So

const myFunction = (myObject) => {
  console.log(myObject.a);
  console.log(myObject.b);
};
Enter fullscreen mode Exit fullscreen mode

can be written as

const myFunction = ({ a, b }) => {
  console.log(a);
  console.log(b);
};
Enter fullscreen mode Exit fullscreen mode

Object shorthand form

Documentation at MDN

When composing objects, we often have incoming parameters from somewhere, and transform them and then return a new object. This can often look like this:

const someDescriptiveName = doStuff(a);
const someOtherDescriptiveName = doOtherStuff(b);

const newObject = {
  someDescriptiveName: someDescriptiveName,
  someOtherDescriptiveName: someOtherDescriptiveName,
};
Enter fullscreen mode Exit fullscreen mode

As you can see, this feels very repetitive. We're assigning the property with the key of a certain name with the contents of a variable with the same name. Luckily there is a shorter way of writing this.

const someDescriptiveName = doStuff(a);
const someOtherDescriptiveName = doOtherStufF(b);

const newObject = {
  someDescriptiveName,
  someOtherDescriptiveName,
};
Enter fullscreen mode Exit fullscreen mode

We can just put the variable name once, and JavaScript will understand that we want a property of the same name as the variable whose value we're using.

Implicit return in arrow functions

Documentation at MDN

When an arrow method only has a return statement, it can be shortened to an implicit form. Quite often we write methods that only return a ternary, or a promise, or the result of a simple calculation. In this case, we don't need a full code block around the function content.

Instead of doing

const multiplyByTwo = (inputNumber) => {
  return inputNumber * 2;
};
Enter fullscreen mode Exit fullscreen mode

we can remove the return keyword and remove the curly braces (or replace them with parentheses if returning an object literal).

const multiplyByTwo = (inputNumber) => inputNumber * 2;
Enter fullscreen mode Exit fullscreen mode

Tip: In Visual Studio Code, you can put the text cursor in the middle of the arrow part of the arrow function and press cmd + . to bring up the Quick fix menu, where you can quickly add or remove the braces from the function.

Calling an anonymous function directly

Documentation at MDN

This is the least used of these four concepts. And possibly also the most confusing. It lets us invoke an arrow function immediately, without assigning it to a variable.

Instead of doing

const myLog = (text) => {
  console.log('Hello ' + text);
};
myLog('world');
Enter fullscreen mode Exit fullscreen mode

we can call it directly without assigning it first

((text) => {
  console.log('hello ' + text);
})('world');
Enter fullscreen mode Exit fullscreen mode

This is very rarely useful, but can be nice in some situations where you need to call an asynchronous method in a context that isn't marked as async.

Back to our confusing line

With these four parts, we can now start deconstructing the confusing line into something that makes sense. If you've already forgotten, that's alright, here it is again:

const result = (({ a, c }) => 
  ({ a, c }))({ a: 1, b: 2, c: 3, d: 4 });
Enter fullscreen mode Exit fullscreen mode

We start from the back, and see that this is an arrow function that's being called immediately. Let's assign the function to a variable and call that instead.

const myFunction = ({ a, c }) => ({ a, c });

const result = myFunction({ a: 1, b: 2, c: 3, d: 4 });
Enter fullscreen mode Exit fullscreen mode

Let's also move the input object to a variable to make it a bit cleaner

const myFunction = ({ a, c }) => ({ a, c });
const myObject = { a: 1, b: 2, c: 3, d: 4 };

const result = myFunction(myObject);
Enter fullscreen mode Exit fullscreen mode

This is already much more readable. But let's keep going. We now direct our focus to the arrow function, where we see that we can start by adding back the curly braces and return keyword.

const myFunction = ({ a, c }) => {
  return { a, c };
};
const myObject = { a: 1, b: 2, c: 3, d: 4 };

const result = myFunction(myObject);
Enter fullscreen mode Exit fullscreen mode

The next step is to remove the destructuring in the function input parameters.

const myFunction = (inputObject) => {
  const a = inputObject.a;
  const c = inputObject.c;

  return { a, c };
};
const myObject = { a: 1, b: 2, c: 3, d: 4 };

const result = myFunction(myObject);
Enter fullscreen mode Exit fullscreen mode

And the final step is to remove the shorthand form of the object returned from our function.

const myFunction = (inputObject) => {
  const a = inputObject.a;
  const c = inputObject.c;

  const newObject = {
    a: a,
    c: c,
  };

  return newObject;
};
const myObject = { a: 1, b: 2, c: 3, d: 4 };

const result = myFunction(myObject);
Enter fullscreen mode Exit fullscreen mode

So there we have it. We have now removed the four magic JavaScript concepts and have something that requires only basic knowledge.

When is complex too complex?

As with most of these kinds of questions, it will vary greatly between different developers and teams. But as a developer your code should always be readable without too much work. But at the same time, we cannot not use the concepts available to us in the language, we just have to know when to use them.

I would write this line as

const pickAC = ({ a, c }) => ({ a, c });
const myObject = { a: 1, b: 2, c: 3, d: 4 };

const result = pickAC(myObject);
Enter fullscreen mode Exit fullscreen mode

This makes it much more readable than the one-liner, while at the same time keeping it short and concise. Calling an anonymous function immediately is a concept so rarely used that – in my opinion – it should only be used when absolutely necessary. But, to each their own, just make sure you agree amongst the team.

Discussion (16)

Collapse
terstuyahmaksim profile image
Max

This is all very funny and cool, but it's bad code because it's hard to read. Good code - no need to document. One glance at the names of the variables makes it clear what should be there and what to do with it. Immediately you need to read it, and more than once. But overall, yes. Very brief and succinct.

Collapse
perenstrom profile image
Per Enström Author

Absolutely. I think that these kinds of functions can be okay in a contained context so that anything using it will call a method with a well understood name, the implementation details are not important for the calling context.

Collapse
mrhiden profile image
Marek Krzyżowski

Actually it is not so unreadable but it is very useful to repack object. I would split only to arrow function pickUp and use that in the next line. This way it is readable and useful. I know there is such a example with pickAC function.

Collapse
alex_alex_0417642415a7f05 profile image
Alex Keeprock

The longer you code the more you appreciate simplicity of a code you see. Cause you don't have a time for such trickery - it only matter how much you ship. Code should be «good enough to read by someone else without explanation», not a nomination for a hackathon «hall of fame».

Collapse
rtpharry profile image
Matthew Harris

Also a typo with myfunction and myFunction in the later snippets 😉

Interesting refresher of some concepts!

Collapse
perenstrom profile image
Per Enström Author

Thank you! Greatly appreciate these corrections!

Collapse
schinta2 profile image
siva sandeep chintamaneni

Might have meant "arrow function" instead of "array function". "To each their own" - can't agree more :)

Collapse
perenstrom profile image
Per Enström Author

Thank you so much! Didn't spend as much time on proof reading this one as I usually do! :)

Collapse
ziadab profile image
Ziad Abouelfarah

I can relate

Collapse
fredgarcia profile image
Garcia

Did it work with var in place of const ?

Collapse
mrhiden profile image
Marek Krzyżowski

Should work, but I would stay with const.

Collapse
perenstrom profile image
Per Enström Author

To be fair, you can write unintelligable code in almost any language. But yes, this line is not that great, thus this article :)

Collapse
johnerincon profile image
John Eduar Rincon G.

Thank you for this clean and clear concepts. I am just a beginner in javascript language and your explanation is very useful. I wlll keep it for my future references.

Collapse
perenstrom profile image
Per Enström Author

Glad to hear!

Collapse
ifarmgolems profile image
Patrik Jajcay

No one ever will use this :)