EPAM Anywhere: 6 Common Mistakes in JavaScript a Mid - Senior Developer Should Know and Be Able to Tackle
Technology / 7 min

6 Common Mistakes in JavaScript a Mid - Senior Developer Should Know and Be Able to Tackle

Bryan_Rodriguez.jpgBryan_Rodriguez.jpg

There are moments when you look back and remember why you began your journey as a JavaScript Developer and how you keep going and diving deeper into more specific and complex topics. When you look back, it is easy to see some of the common JavaScript mistakes you made as a junior developer and you retain them as part of your deck of common JavaScript errors so you already know how to deal with them.

Some common problems with JavaScript

There is a long list of possible mistakes you could face and things you should be aware of in order to avoid JavaScript bugs, if you are a mid or senior developer and are working with modern JavaScript (latest ES versions to current date October/November 2021). We will briefly mention below just a few of them to help you tackle some important issues and reduce the technical deb and bugs in Javascript.

Common JavaScript mistake #1: Working without a Coding style guide (rules)

Probably the mother of all bugs in JavaScript begins when you start a project without defining a JS Coding style. It’s very common to have certain rules with your team regarding how to write the JS code in a uniform way. It is important to remember that there are several bad practices and common JavaScript errors that are actually included by default in these styles. If you don’t address this upfront, the code written by the team members will vary considerably and will lead to several errors. These JavaScript bugs will be difficult to notice and be reviewed from a pull request (PR) before being actually merged with the main/master base code.

So the idea is to use a linter to ensure that all members of the team follow these rules and avoid common JS mistakes. Here is an example of Standard and AirBnB style code:

Standard style

Rule > eslint: no-this-before-super 

Description: super() must be called before using this.

class Dog extends Animal {
  constructor () {
     this.legs = 4     // x avoid
     super ()
  }

Link to all the Standard rules: https://standardjs.com/rules.h...

Airbnb style

Rule > 3.4 Use property value shorthand. ESLint: object-shorthand

Why? It is shorter and descriptive.

Code:

function getKet(k) {
  return 'a key named ${k}';
}
// bad
const obj = {
  id: 5,
  name: 'San Francisco',
};
obj[getKey('enabled')] = true;
// good
const obj = {
  id: 5,
  name: <p>'San Francisco',
  [getKey('enabled")]: true,
};

Link to all the AirBnB rules: https://github.com/airbnb/Java...

To include these marvelous killers of bugs in JavaScript, the best method is to use the ESLint package. The key, though, is to define with your team which style of code will be chosen and maintained throughout the project.

Your decision will be configured using the ESLint rules, or you could base it on current renamed JS style guides like Standard, AirBnB, or Google.

Additionally, it’s critical to see if that was successfully integrated with the IDE used by all the team members in order to verify whether or not the written code has style errors. A better approach might be to use an automatic tool to refactor the code according to the rules like Prettier (probably the unopinionated tool for that purpose).

ESLint link https://eslint.org/

Prettier Link: https://prettier.io/docs/en/integrating-with-linters.html

There are the statistics of today's usage (October 2021)

Statistics.png
Source: https://www.npmtrends.com/eslint-config-airbnb-vs-eslint-config-google-vs-standard

A bonus here could be the use of code analyzers, also known as static code analyzers. Some of the most killer features are the ability to:

  • check code smells (sometimes checked with linters but sometimes not)
  • check code complexity

One of the most used code analyzers is probably SonarQube, but there are others that can do a good job too.

JavaScript Mistake #2: Using magic values

Using magic values is a common mistake in multiple languages, but in JS it’s definitely a mistake you should avoid. First of all, a magic value is considered a constant value that magically appears in the code, and probably only those who wrote the code could easily explain what it does, and usually, even they forgot it. XD

Example:

  • with magic values
const specialFn = (r) =>{
   const fnValue= 2*3.1416*r // not clear :/
   return fnValue
}
console.log(specialFn(4))
  • the right way
const PI = 3.1416
const specialFn = (r) =>{
   const fnValue = 2*PI*r // Looks like circumference :)
   return fnValue
}
console.log(specialFn(4))

That approach basically applies to everything that could be considered a constant, not only numbers but to names, prefixes, etc.

JavaScript mistake #3: Disregarding object mutability (or mutating in a cluttered way)

Const does not imply that the object is not mutable, as some people assume.

Not_mutable.png

An available solution to avoid object mutation in JS is using Object.freeze() as follows:

Solution_to_avoid_object_mutation_i.png

More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze

The issue with mutability does not end here. If you want to be more strict with mutability, there are many other considerations to keep in mind.

For example, the use of this mutability approach requires extra space because it involves several clones of mutated data. Although this approach is commonly used in Functional Programming approaches, it is important to be aware of the spatial complexity to avoid memory leaks, as will be discussed in mistake #6.

JavaScript mistake #4: Failing to handle errors correctly

Handling errors correctly is not limited to using the try-catch statement. It is very important to recognize how to use the exceptions and how to catch and handle the errors in a clever and safe way.

To do so it’s important to recognize there are some differences between an Error and an Exception, even though r most of the time, JS developers refer to them as if they are practically the same.

An Error can be considered as an unexpected response of the program. An Error can happen in different phases, for example during compilation time (usually Syntax errors), or running time also called runtime errors.

Examples of execution errors in JS:

  • RangeError: invalid array length
const invalidArrayNegative = new Array(-1)
const invalidArratDecimal = new Array(1.2)

Link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length

  • TypeError: 'x' is not iterable
let nonIterrable = 123
for (let number of nonIterrable){
  console.log(number)
}

Link: 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/is_not_iterable

In addition to runtime errors, there is another type called logical errors, which are related to how the program is expected to work.

But it is important to clarify that an error can actually crash the execution of a script or the call of an event handler, but the correct catch will transform these errors/exceptions (JavaScript throw error) into a handled exception.

Exceptions are considered errors that are expected or almost expected to be handled in order to avoid the interruption of the flow of the program execution.

Example of an exception catch and handling:

let nonIterrable = 123
try{
  for (let number of nonIterrable){
    console.log(number)
  }
}catch(e){
  if(e instanceof ReferenceError) { console.log("Handle in some way...")}
  else console.log(e.message, e.name)
}

This is a good example of how to handle an error as an exception using the specific type of error to handle specific types of bugs in a certain way, and others differently.

Other kinds of errors include syntax errors and type errors, which are usually prevented using linters, as mentioned above, or using the strict mode, and transpilers (like Babel for example).

The need to customize error types allows for better handling of these special cases into tailor-made exceptions. Since ES6 there is an easy way to do it, extending the Error Object and creating custom error types.

class MyOwnError extends Error {
  constructor(message='Custom error'){
    super()

    this.name = 'MyOwnError'
    this.message = message
   }
}
try{
  throw new MyOwnError('Special exception')
}catch(e) {
  if(e instanceof MyOwnError) console.log('Custom exception:',e.name, e.message)
  else console.log("other error", e.message)
}

More info:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#custom_error_types

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Control_flow_and_error_handling#exception_handling_statements

JavaScript mistake #5: Do not use any kind of typing or “static typing”

It is important to say that’s not a JavaScript bug by default, but, as everyone should know, JS is a dynamic typing language, also called a loosely typed language. There are plenty of discussions about whether it's beneficial or not, but if you spend some time writing JS code, you know that a loosely typed way to write functions can lead to some common JavaScript errors due to the ambiguity of the types and the absence of contracts (interfaces) to improve the consistency and type consistency between code components.

That could be covered in a separate article, but for now, just know that there are options to avoid some bugs in JavaScript resulting from the absence of types.

The first one is to use Typescript. Since TS was released, its main objective was to provide static typing to JS using several syntax sugar features to guarantee the types should be maintained and for developers more familiar with other languages. It’s important to mention that TS is basically a superset of JS, which means, you could use JS inside TS because TS is JS too, but with types and some syntax sugar added.

Example of Typescript (left) and transpiled JavaScript code (right):

Typescript_and_transpiled_JavaScript_code_.png

There are other options, like using Flow directly with Vanilla JS, and more rudimentary approaches using JSDocs as part of the IDE, which is basically using JS with the types working like a lint inside the IDE.

JavaScript mistake #6: Creating memory leaks

A memory leak is easy to recognize at runtime when you monitor your server instances and see the average memory consumption increase. The first thing someone might think of doing is increasing the default memory limit of Node JS from the 1.4Gb using the classic environment variable modification like this:

'NODE_OPTIONS="--max-old-space-size=2048"'

But that never solves the main question, which is “why is the memory consumption so high”?

There are several articles regarding this situation, and here, I will cover just one very common case based on my experience.

You probably made a “functional” code, which usually works well for a basic unit test, but what if you included only the happy pad test cases, and some edge cases, could that be the cause of the memory leaks?.

That’s the point of using arrays to manage collections, saving in-memory massive collections like arrays to iterate them afterward.

Code example:

const arr = Array.from({ length: 1000 }).fill(0);
const arr2 = arr.map((el)=>{
  // Perform some transformation
  return el+1
});

//...
//othrecode
//...

const arr3 = arr2.map((el)=>{
  // Perform some transformation
  return el+3
});
console.log(arr3)

Link: https://jsbin.com/vetulav/edit?js,console

The example above is a good way to show how with a map we can perform a simple transformation for a map, but cloning it. For a little array, the memory consumption is insignificant, but with greater arrays, we would easily find a big memory impact.

Solution: Understand that you are exceeding your space complexity for big arrays (this is a CS concept we didn’t cover here but that could be covered in another article). A good idea is to split in chunks the arrays you are working with. Identify possible extra copies of those arrays, understanding where a no mutation approach with a map would be better, or where an in-place transformation would be best (unless you have to do it with an FP approach).

So, another good option is to chain the maps, avoiding multiple clonings:

const arr = Array.from({ length: 1000 }).fill(0);
const arr2 = arr.map((el)=>{
  // Perform some transformation
  return el+1
})
map((el)=>{
  // Perform some transformation
  return el+3
});

//...
// other code
// if is possible
// to refactor it
//...
console.log(arr2)

Link: https://jsbin.com/yulicix/edit?js,console

Or, if possible, reduce the number of maps and cloning of these arrays using a reducer approach like this:

const arr = Array.from({ length: 1000 }).fill(0);
const arr2 = arr.map((el)=>{
  // Perform some transformation
  return el+4
});
console.log(arr2)

Link:https://jsbin.com/dodoliy/edit?js,console

<p>const arr = Array.from({ length: 1000 }).fill(0);
const arr2 = arr.reduce((acumulator, current_el) =>{
  // Perform any other kind of transformations and
  // even agregations
  acumulator.push(current_el+4)
  return acumulator
},[]);
console.log(arr2)</p>

Link: https://jsbin.com/fosoyoj/edit?js,console

More info about memory management https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management

Wrap-up

There are a lot of potential problems with JavaScript, most of them you can spot with a quick glance, but not all of them. We’ve discussed 5 of the possible mistakes we can fall into as developers. Instead of focusing on specific JavaScript errors, I tried to address general mistakes that are usually familiar after some time in your JavaScript journey.

I strongly recommend the use of linters and static code analyzers as a good strategy to avoid some of the most common JavaScript errors. And give your team sufficient rules to help avoid a lot of mistakes and write uniform and beautiful code.

In addition, static typing could be a good choice, bearing in mind that the implementation of static typing could be progressive and could help you avoid a lot of headaches with a minimum amount of effort.

Keep in mind that these suggestions are not a silver bullet. There are a number of other possible JS mistakes that are harder to recognize. Only experience, constant learning, and a good domain of JS particularities can detect, as well as a good error/exception handling strategy, which is certainly the second most important advice to keep in mind.

I also want to mention that there are other things, like the use of design patterns (GoF23), or avoiding anti-patterns, and also some best practices and recommendations of Software Author Gurus like “Clean code” by Robert C Martin, or “Refactoring” by Martin Fowler will help you avoid not only common JavaScript mistakes but also mistakes in any programming language you choose.

Written by

Bryan_Rodriguez_‎-_Photos.pngBryan_Rodriguez_‎-_Photos.png

BryanLead Software Engineer, Colombia

I'm from Colombia currently working at EPAM Anywhere. I have several years of experience working as a Software Engineer in the whole software development cycle. My main stack is on Javascript, but I like Python and Java too. 

I have strong skills in Computer science, Database design, and some experience in Data Engineer tasks and I consider myself a Machine learning enthusiast. 

Just now, I'm working on an e-commerce project, and I'm involved as a mentor of the Global Node JS mentoring program of EPAM, and running some courses in my career path to enhance my skills as a tech lead!

Are you searching for a remote job in IT?

We're inviting Software Developers, QA Engineers, DevOps Specialists, Business Analysts, Designers, Data Analysts, and other IT specialists to join our community and work from the comfort of your home.

View jobs