loading...
Cover image for Writing modern JavaScript code

Writing modern JavaScript code

scastiel profile image Sébastien Castiel Originally published at blog.castiel.me ・5 min read

Remember when JavaScript was a language used to make elements change on the page when the cursor was over them? These days are over, every language evolves over time, and so does the way we use them. Look at the code you wrote one or two years ago: do you feel ashamed? If yes, this post is for you 🙂

I'll try here to list some good practices to make your JavaScript code easier to write, read and maintain.

Use a linter that can format your code

The first advice I'm giving you is to use a linter that will check you respect some rules that make your code consistent from a file to another, especially if you're several developer to work on the same project: indentation, spaces in parenthesis, replace == by ===

But more important, make your linter fix automatically your code for you when possible. ESLint does that very well (with the --fix option), and it's well-integrated with all major IDEs to auto-fix files on save.

You can also use Prettier which is more focused on formatting than linting, but the result is basically the same 😉

Next point will help you chose what rules to use with your linter:

Use modern rules for your linter

If you wonder what rules to want for your code, here is a hint: StandardJS. It's a very strict linter that won't give you any choice in the rules, but each of them is more and more admitted by the community. Here are some examples:

  • use 2 spaces for indentation (I used to use 4 spaces, but actually using 2 is quite nice)
  • no semi-colon (very weird at the beginning, but few days later I couldn't go back)
  • spaces after keywords (like if) and in curly braces, not inside parenthesis
  • and a lot more.

StandardJS is a standalone Node module that can lint and fix your code, but if you want to use it in a big existing project and deactivate some rules (because some would need a lot of modifications), you can also use the ESLint predefined config. For instance I deactivated the rules no-mixed-operators and import/no-webpack-loader-syntax.

Use ES2015+ new features

If you develop with JavaScript there's no way you haven't heard of ES2015+ (or ES6, ES7…) features. Here are the ones I couldn't live without anymore:

  • arrow functions: writing functions like x => x * 2 is very useful with functional programming (see next point)
  • classes: stop using prototype functions, classes are so much cooler 😉 (but don't abuse, JavaScript is so much better than any object-oriented language)
  • operations with arrays and objects:
function doSomething() {
  const a = doSomethingElse()
  const b = doSomethingWithA(a)
  const otherResults = { c: '😺', d: '🐶' }
  return { a, b, ...otherResults } // equivalent to { a: a, b: b }
}
const { a, c, ...rest } = doSomething() // Also works with arrays!
// `rest` looks like { b: ..., d: '🐶' }
  • making promises easier to use with async/await:
// Please try to write the same code with classic promises ;)
async function doSomething() {
  const a = await getValueForA()
  const b = await getValueForBFromA(a)
  const [c, d] = await Promise.all([
    // parallel execution
    getValueForC(), getValueForDFromB(b)
  ])
  const total = await calculateTotal(a, b, c, d)
  return total / 1000
}

Wonder how to use these fantastic features? One of my articles gives you some advice! (By the way, with latest version of Node.js you probably won't need Babel anymore to use the greatest new features 😀)

Use functional programming

Very hype right now, functional programming is gaining a lot of success recently, not only in JavaScript. The reason? It makes the code more predictible, safer, deterministic, and a lot easier to maintain when you're used to it. Here are some simple advices:

First stop using for loops. In most (every?) case you don't need them. For instance:

const arr = [{ name: 'first', value: 13 }, { name: 'second', value: 7 }]

// Instead of:
const res = {}
for (let i = 0; i < arr.length; i++) {
  const calculatedValue = arr[i].value * 10
  if (calculatedValue > 100) {
    res[arr[i].name] = calculatedValue
  }
}

// Prefer:
const res = arr
  .map(elem => ({ name: elem.name, calculatedValue: elem.value * 10 }))
  .filter(elem => elem.calculatedValue > 100)
  .reduce((acc, elem) => ({
    [elem.name]: calculatedValue,
    ...acc
  }), {})

Okay I admit this is a very extreme example, and if you're not used to functional programming it may look more complicated. Maybe we can simplify it:

const enrichElementWithCalculatedValue =
  elem => ({ name: elem.name, calculatedValue: elem.value * 10 })
const filterElementsByValue = value =>
  elem => elem.calculatedValue > value
const aggregateElementInObject = (acc, elem) => ({
  [elem.name]: calculatedValue,
  ...acc
})
const res = arr
  .map(enrichElementWithCalculatedValue)
  .filter(filterElementsByValue(100))
  .reduce(aggregateElementInObject, {})

Here we defined three functions that basically exactly what their names say. Second advice: create local functions (even in existing functions) to document your code without comments!

Note that the three local functions don't modify context they're executed in. No external variable is modified, no other service is called… In functional programming they're called pure functions. They have some great advantages:

  • they're easilly testable because from given parameters, there is only one possible result, even if we call the function several time;
  • they're guaranteed to give the same result no matter the actual state of the application;
  • the application state stays the same before and after the function call.

So my third advice: use pure functions a lot!

Some other advices to finish

  • get used to work with asynchronous code, use promises a lot, look at observales with RxJS (there is a great tutorial about functional programming leading to reactive programming)
  • write tests! Should seem obvious, but I know a lot of projects have untested code, although testing JavaScript (front or backend) is not as difficult as it seems.
  • use latests features of the language: for instance stop writing arr.indexOf(elem) !== -1 in favor of arr.includes(elem).
  • read a lot of technical articles: the JavaScript subreddit is a very good source to know the coolest practices in the ecosystem.

Oh and to conclude, the greatest advice I can give you: always refactor your code! Making improvements to a module you wrote one year ago? Take the opportunity to replace var with const, to use arrow functions or async/await to simplify the code… It's always nicer to work on code you like 😉

This article was originally posted on my blog.

Discussion

markdown guide
 

Great post! I want to take JS code a new level, I guess you have given me something keep in mind a lot.

 

Prettier lints like crazy. It enforces an absolute coding standard. It's main virtue is that it only has two configuration options, tabs vs spaces and quotes vs apostrophes. Otherwise, everyone who uses it gets standard, well-formatted code. I am a huge fan.

 

You're confusing functional programming with extreme immutability, which is an anti-pattern by itself, generating an incredible amount of overhead.
You're literally advocating to loop three times instead of one just to force yourself to use map, filter and reduce.

I understand these are examples, but they're extremely bad examples for any apprentice dev so this needs to be clarified.

While the post correctly encourages having immutable data, the idea is to keep that data updated with pure functions. Now, pure functions must avoid shared state, mutable data, and side-effects outside of their bounds but not necessarily inside, this simple concept may be hard to grasp at first, so let me try to show it with the examples you used:

// Instead of:
const res = arr
  .map(elem => ({ name: elem.name, calculatedValue: elem.value * 10 }))
  .filter(elem => elem.calculatedValue > 100)
  .reduce((acc, elem) => ({
    [elem.name]: calculatedValue,
    ...acc
  }), {})

// Prefer:
const res = (() => {
  let obj = {}
  for (let i = 0; i < arr.length; i++) {
    const calculatedValue = arr[i].value * 10
    if (calculatedValue > 100) {
      obj[arr[i].name] = calculatedValue
    }
  }
  return obj
})()

Of course it may or may not be better to use Object.keys().forEach() or even Object.keys().map() and still loop once doing all the needed calculations on the fly as well, but the point is that yes, pure functions and functional programming should be used, but we must always have in mind that both of them can use and abuse of mutable data correctly contained and that's not bad, it's actually one of the beauties of Javascript.

 

Can you give a better example without chaining together map, filter, and reduce but still keeping it functional? Why is it an anti-pattern?

 

A nice little article but your first code example for functional programming is misleading.

The array declaration used for both // Instead of: and // Prefer: is shown visually to be a part of the // Instead of: block.

This might suggest to the user at a glance "oh, the new way is prettier". But it's a misrepresentation of what's going on.

Sorry to be a neg, but you're going to sell an idea, you gotta sell the truth.

 

You're absolutely right, my bad. It's fixed 😉

 

Great Post! Love the JS code without the semi-colon. I'm definitely gonna install StandardJS

 

what a great advice to replace == by ===... NOT!

this will render a lot of code as unfunctional.

 

I would argue that in a lot of cases, code that breaks from changing == with === probably stands in need of some improvement anyway. Just IMHO

 

Valid exception to the rule, but unless you're going to update and change it, you probably don't need to lint legacy code, which was the context in which the original post mentioned replacing == with ===

 

If you map, then filter, then reduce an array in code that I manage, you're not getting your code in.

 

whilst array.map and array.forEach are unarguably more readable than for loops, aren't for loops still considerably faster in most browsers?

 

I was thinking of the same. Found this relevant reddit discussion.

I think this is something browsers could optimize. Or probably they already do.
I wonder if there are any benchmarks around.

 

"The reason? It makes the code more predictible, safer, deterministic, and a lot easier to maintain when you're used to it."

Can you elaborate why? I have never programmed in a functional way in a serious project so I would like to know the reason behind those affirmations.

Thanks.

 

One of the keys to functional programming is no side effects. So code inside the function shouldn't affect things outside of the function.

Another key of functional programming is that functions should not be affected by things outside of it's scope. (ie: For any given set of parameters the function will always have the same return for that set of parameters).

Keeping these in mind will make code more predictable and safer.

 
 

Greetings! I'm new to this... How should I code this in a better way? What should I change?

pastebin.com/c6zfhf6F

 

Nice! It's a really good tool. I'll use it and it's perfect for a collaborative project