Codementor Events

Your guide to concise JavaScript

Published Jan 15, 2019Last updated Jul 13, 2019

The easiest code to maintain is the code you never write

You probably know this saying. I always liked to keep my code clean and concise, hoping that this will help the people who will take care of the project in the future. It would benefit me as well if I would maintain that project, because I might forget some aspects and having an easy to read codebase gives everyone a good head start.
Let's see how can you write less JS code and make an easier to read/maintain/debug project.

Note:this article presents a collection of well known concepts and some relevant examples.

Table of contents:

  1. Template strings
  2. forEach, map and reduce
  3. Arrow functions
  4. async / await
  5. Utility libraries: lodash, underscore

1. Template strings

This is an easy one and you probably already use it. Instead of using + to concatenate strings and variables, you can use the back-tick (`) syntax.

console.log('My name is ' + name + ' and I\'m ' + age + ' years old.')
// becomes
console.log(`My name is ${name} and I'm ${age} years old.`)

Note how ${} is used to insert variables into the string template.

You can always do basic operations or use the conditional ternary operator within the curly braces:

console.log(`x + y = ${x + y}`)
console.log(`You ${ age >= 18 ? 'can' : 'can\'t' } drive`)

Another cool thing you can do with this syntax is creating multiline strings.

const multiline = `
  This is just another
  multiline
  string.
`

More info about template strings.

2. forEach, map and reduce

a. forEach

It's been a long time since I've written a for loop in JavaScript. Let's compare it with forEach.

const books = [
  {name: 'How to win friends and influence people', year: 1936},
  {name: 'The Four Steps to the Epiphany', year: 2005},
  {name: 'Brave New World', year: 1932}
]

// traditional for loop
let step;
for (step = 0; step < books.length; step++) {
  console.log(books[step].name);
}

// forEach loop
books.forEach(function(book) {
  console.log(book.name);
});

forEach will work on every array in JavaScript.

More about forEach.

b. map

map works on arrays (like forEach) and it returns a new array obtained from calling a provided function on each element in the initial array. Of course you could do that with for or forEach but map provides the simplest syntax.

let upperCaseBookTitles = [];
books.forEach(function(book) {
  upperCaseBookTitles.push(book.name.toUpperCase());
});

console.log(upperCaseBookTitles)
// ['HOW TO WIN FRIENDS AND INFLUENCE PEOPLE', 'THE FOUR STEPS TO THE EPIPHANY', 'BRAVE NEW WORLD']

upperCaseBookTitles = books.map(function(book) {
  return book.name.toUpperCase();
});

console.log(upperCaseBookTitles)
// ['HOW TO WIN FRIENDS AND INFLUENCE PEOPLE', 'THE FOUR STEPS TO THE EPIPHANY', 'BRAVE NEW WORLD']

More about map.

c. reduce

reduce is really useful when you need to reduce an array to a value/object. It applies a so called reducer function (provided by you) on each value in your array.

Let's say we want to get the newest book in our array. This is how you do it with reduce. (this is a silly example, but you get the idea)

let newestBook = books.reduce(function (acc, cur) {
    if (acc.year < cur.year) acc = cur;
    return acc;
}, {year: 0});

First parameter of the function reduce is the reducer function. It has 2 parameters:

  • acc = is the accumultor - where we put the value we're trying to obtain
  • cur = is the current array item accessible at each iteration
    The second parameter of reduce is the initial value of the accumultor.

As I've stated before, we could obtain the same result with for or forEach.

More info about reduce.

3. Arrow functions

Some examples first.

function() {}
// becomes
() => {}
function sum(a,b) {
  return a + b;
}
// becomes
const sum = (a,b) => a + b // notice how return and the curly brackets can be dropped if the function body has only one line
function (x) {
  return x;
}
// becomes
(x) => { return x; }
// or even better
x => x // the brackets are not mandatory if the function has only one argument

In a nutshell the arrow functions have been introduced to offer a shorter syntax and to solve the multiple this issue (which occurs especially when doing OOP)

Now let's see how we can use this syntax with the forEach, map and reduce functions.

forEach

books.forEach(function(book) {
  console.log(book.name);
});
// becomes
books.forEach(book => console.log(book.name));

map

upperCaseBookTitles = books.map(function(book) {
  return book.name.toUpperCase();
});
// becomes
upperCaseBookTitles = books.map(book => book.name.toUpperCase());

reduce
We can transform the reducer by using the conditional ternary operator and get a one-liner.

let newestBook = books.reduce(function (acc, cur) {
    if (acc.year < cur.year) acc = cur;
    return acc;
}, {year: 0});
// becomes
let newestBook = books.reduce((acc, cur) => acc.year < cur.year ? cur : acc, { year: 0 });

At this point I think you can get the idea of how much you can achieve by combining forEach, map and reduce with arrow functions.

More about arrow functions.

4. async / await

The callback hell is real. You probably found yourself in the situation where you had to pass callbacks around to make sure the code gets executed at the right moment. That's fine, but if the project grows you (and others) will have a hard time reading the code.

Let's see it in action!
Note: Consider getResourceA, getResourceB and getResourceC are async functions.

getResourceA(function(error, resourceA) {
  if (!error) {
    getResourceB(function(error, resourceB) {
        	if (!error) {
            	getResourceC(function(error, resourceC) {
                	if (!error) 
                    	console.log('Resources', resourceA, resourceB, resourceC);
                    else {
                    	// do something with error
                    }
            	})
      } else {
            	// do something with error
            }
    })
  } else {
    	// do something with error
    }
})

The first solution for the callback hell was the Promise. I've used promises a lot in my work and it's a nice solution and I definitely recommend trying it. But now we'll focus on async/await.

First step is to define your async functions using the async keyword.

async function getResourceA() {
  try {
    	let users = await fs.readFile('/etc/passwd', 'utf8');
        // note: readFile is async and it can be called with await
        // you can use await only within an async function
    } catch (err) {
    	throw err;
    }
    return users;
}

async function getResourceB {...}
async function getResourceC {...}

Then, all you need to do is to call the newly created async functions using await.

try {
  let resourceA = await getResourceA();
  let resourceB = await getResourceB();
  let resourceC = await getResourceC();
    
  // use resources as you wish
} catch (err) {
  console.error('Something went wrong: ', err);
}

It's a good practice to wrap your async calls in try-catch blocks.
I'll let you compare the code and choose which one you like.

More about async / await.

5. Utility libraries: lodash, underscore, ramda

I strongly recommend you to check underscore or lodash. If you're wondering what's the difference between them check this answer on StackOverflow.

You can see lodash / underscore as a toolbox of advanced functions ready to help you in various scenarios, like: working with arrays, objects or strings. Have a look on their docs and you'll finde some really powerfull functions.

Personally, I started experimenting with ramda because I've become interested in functional progreamming. I can't recommend enough this blogpost series called Thinking in Ramda.

Why using a utility library?

First of all, you'll end up writing less code. Equally important, it forces you to comply to their API and this results in much cleaner and readable code. It may also inspire you to create your own way coding style rules.

Conclusions

Less is more. But you shouldn't compromise clarity for the sake of writing less code. A good developer doesn't write code that's recognizable only for him/her; a good developer writes code which can be understood by a team, or by the developer who will continue his/her work.

Stay informed, experiment new things and keep those which work for you.

Let me know what's missing in this list. What other techniques are you using to write concise JS?

Code on!

Discover and read more posts from Catalin Tudorache
get started
post commentsBe the first to share your opinion
shiba
5 years ago

Thanks for sharing!

Catalin Tudorache
5 years ago

You are welcome!

marcos alonso
5 years ago

Thank you

Catalin Tudorache
5 years ago

You are welcome!

f rakw
5 years ago

Can repl run discord music bot(YouTube music) ?

Show more replies