Javascript Worst Practice

Bad coding habits you need to stop right now

Fernando Doglio
Bits and Pieces
Published in
12 min readSep 27, 2021

--

Everyone talks about the “best practices” to follow around different technologies, and sometimes we tend to listen to that advice. However, what about the things we’re doing and we’re not even aware that they’re not only wrong, but terrible practices?

Let’s take a quick look at 5 of the worst things you can do to your code while writing JavaScript.

Taking advantage of the language’s implicit type coercion rules

Type coercion can be fun, it allows you to write code that works without having to worry about the type of data you’re dealing with. This is of course, only fun if you actually know the type coercion rules of JavaScript by heart.

And given how they’re not that clear to experienced developers, assuming you and others using your code, do is a big deal.

Type coercion, in case you’re not aware of it, is the mechanism JS has to try and force the types of variables when you’re using several together and they’re not particularly compatible.

For example, trying to use the + operator between two numbers is easy right? Two numbers get added up, two strings get concatenated, but what happens if one of them is a number and the other one is a string? Some rules dictate exactly what happens to every type when used with a different one.

Those are the type coercion rules, and once you know them, you can take advantage of them. The question is, should you?

Because not everyone is fully aware that 2 + '2' is not going to be 4. And that the type of [] + 1 is neither Number nor Array.

JavaScript has the amazing feature of allowing us to ignore types when assigning values to variables, but that doesn’t mean we should ignore types altogether. Sometimes doing some type-checking or keeping a certain naming standard can help others to avoid doing some really strange operations by accident. Otherwise the parser will let you do them, and the engine will do its best to provide some form of result.

Another very common operator that is very affected by type coercion, is the == operator. We tend to use this one a lot but we gotta remember that with only two equal signs, the JS engine will try to force types for the values to match. For example, the number 1 is equal to the string "1" and an empty array is equal to an empty string. However, I’m sure that when you wrote that equality expression you were not hoping for those results to be true, were you? And if the answer is “I don’t care really”, check out this silly example:

What do you think would happen if we execute the above code with a being a number, a string or a boolean?

The same logic with a number, a string, and a boolean

On the first image I assigned the number 1 to a , then I assigned the string "1" and finally the value true . There was never a syntax error nor a runtime error. The engine did the best it could. Notice how the + has a tendency to turn numbers into strings when used together, but the - operator is the other way around. The point here is that true is conceptually a different thing than 1 and true == 1 should not be valid. Instead, to avoid this type of confusion, a good idea is to use the strict equality operator as much as possible: ===

This operator will, amongst other things, take into account the type of values being compared. So it’s a lot safer and leads to a more robust logic.

And when it comes to typecasting, or the act of manually forcing the type of a value into another, try to make it as explicit as possible. That way whoever is reading your code gets the syntactic clue as to what’s happening instead of having to guess when it happens and why.

Look at the following examples:

You can also turn any other value into a string by calling its toString method. Yes, they all have one. Even your custom objects will implicitly get that method (which you can overwrite BTW to provide a more accurate string representation of your objects). Sadly there is no “to[INSERTYPE]” method for every type, but when in doubt go with the constructor of the type like I showed for Number , that’ll be a clear clue to whoever is reading your code that you’re casting a value there.

Remember, the code needs to run smoothly on the computer, but it also needs to be read by humans!

Tip: Build with independent components, for speed and scale

Instead of building monolithic apps, build independent components first and compose them into features and applications. It makes development faster and helps teams build more consistent and scalable applications.

OSS Tools like Bit offer a great developer experience for building independent components and composing applications. Many teams start by building their Design Systems or Micro Frontends, through independent components.
Give it a try →

An independently source-controlled and shared “card” component. On the right => its dependency graph, auto-generated by Bit.

Using var in 2021

I mean, honestly, what rock have you been living under for the past 6 years?

Unless you’re working for a very specific client that is using a grossly outdated and no longer supported JS engine, there is no real valid excuse for you to still be using var in 2021.

The good old var only had 2 scopes: functional or global. Meaning whatever you defined with it inside a function was accessible to the entire function, no matter where you declared it. Or it was global if you did it outside. The other interesting behavior of var is that it would not yield an error if you redeclared an existing variable, it would just do nothing about it which could potentially lead to confusing logic if you redeclared the same variable twice inside the same function doing different things with both versions.

The current and more refined alternative is to use either let or const , having the second option used for immutable values (i.e constants). The main benefit of let over var is that the former now boasts a lexical scope, which means it’ll only be accessible within the block of code you declared it. Considering that a block of code is whatever you write between { and } , you could declare variables that only live inside an IF statement’s body. Or inside the code of a FOR loop. And you could always use the same name without fear of having some problem with clashing values.

The whole point of ditching var for let is to give you, the developer, more granular control over who you define your variables. Mind you, you can perfectly code with var right now and everything will work, as long as you don’t get the names repeated or unintentionally declare something global and then reference it somewhere else without first declaring it. It’s a big IF I know, but it could happen. The new and shiny let allows you to avoid that problem altogether. That’s all.

Not understanding the differences between normal functions and arrow functions

As part of my job I do tech interviews with people who’re looking to join the company I work for. And while I don’t work with JS daily now, if you put Node.js or JavaScript in your resume, I’m going to be asking about it. And there is a 100% chance that I’ll ask about the differences between normal functions and arrow functions.

And the main thing I see is that a lot of people don’t know them! The arrow syntax is not syntactic sugar people! Get on with the times and read the f’ ing docs!

There are actually several differences outside of the syntax you use to define them and you should know them all by heart at this point. They’ve been around for 6 years already, so if you’re not up with the standard, it’s on you.

The biggest differences between arrow functions and regular functions are:

  • Arrow functions don’t have their own context. This is especially important if they’re defined inside another function. For example, as a callback for a forEach execution. If inside the arrow function you use the keyword this you’re referencing the context of the parent function. This is a big difference because before arrow functions, you’d have to save a reference to this before calling the callback function, and within the callback use the new reference, otherwise the this inside it would be a completely new context.
  • Arrow functions don’t have the arguments especial hidden variable. Inside a regular function you can call a variable you never defined called arguments which contains an array-like object with all the arguments received during the function’s execution. This is great if you wanted to create a function that would receive a variable amount of attributes. Arrow functions don’t have access to it, however, now thanks to rest parameters, this is also not needed anymore.
  • Arrow functions are not valid constructors. Believe it or not regular functions are “constructible”. Meaning they can be called with the new keyword and return a new instance of that function. Why is this possible? Because back then, before ES6, we also had prototypal inheritance as our main way of working with objects, and functions (regular functions) were the constructor for them. However, now with classes that is no longer the “way to go”, and arrow functions reflect that change in direction. Arrow functions while callable (just like regular ones), are not constructible, so you can’t really put a new before calling them, it won’t work.
  • Finally, the last minor difference: arrow functions don’t allow duplicated argument names. This is acceptable for regular functions in non-strict mode. However this can’t happen with arrow functions, regardless of how strict you want to be.

There you go, that’s a big “no-no” that you’re no longer guilty of. You’re welcome.

Not understanding THIS by now

If you’ve been working with JavaScript for a while now, you’ve most likely had to deal with the this keyword. And while it might not have been as painful as before because you were probably using arrow functions, are you sure you actually understand what it is and how it works?

There are two things to remember when using this :

  1. Everything in JS is an object. That means your functions are objects and your methods are objects too.
  2. The this keyword is not specific to code within a class definition. You can use it anywhere, including function bodies. Heck, you can even use it outside on the global scope, and you’ll get some interesting results.

Now with that in mind, the this keyword references the current execution context of the function from where you’re calling using it. That means:

  • If you’re using it inside a regular function, it’ll be the execution context of that function. Now that prototypal inheritance is no longer the preferred way of dealing with objects, this is not a very usual use case.
  • If you’re using it inside an arrow function, then it’ll reference the execution context of the parent function. That is, if you’re calling this arrow function within a regular function, the latter will act as a parent function. If you’re using the arrow function at the top level, then you’re accessing the global scope through this .
  • If you’re using it inside a method, you’re referencing the execution context of that method, which also includes all the properties defined as part of the class of that object.

Those are the rules, and by knowing the rules, and understanding the fact that you can also overwrite the value of this from outside all those places, you can do some interesting things.

For instance:

What do you think will happen when you run the above example? And why is that?

I’ve declared a very simple class, and a very simple function. However, the function does not accept arguments, instead, it directly references 2 properties within its execution context. The problem? The properties have never been defined in the function’s particular context. They are part of the class though.

So by using the call method that every function has, you can call it by overwriting its context. Thus allowing this external function to act as a method of the Person class.

Using this method of context override, you could create code that works with objects from outside, without having to modify their implementation. The question you’d then have to ask yourself is: do I want to do this? This technique gets real close to metaprogramming and when you go down that rabbit hole, many of the common “best practices” no longer apply, so take that with you and think about it.

The point is that you should now understand what it is and how to take advantage of the this keyword.

Misusing closures and causing (unwanted) memory leaks

This one is tricky because memory leaks are a difficult concept for JS developers consider we tend to not think about memory management most of the time.

That being said, closures, while a great tool to utilize often, are great at keeping the door open for them memory leaks to come in and settle down in our code.

So let’s quickly cover a common cause of memory leakage due to incorrect use of closures.

And before that, a closure is a JS function that gets defined by another function. In that situation, the new function gets “linked” to the function scope of the parent function so that if there is code inside the former that references variables defined within the latter, it can still reference it even after the latter has been destroyed. Let’s see a quick example:

Function X returns an anonymous function, which in turn prints the content of a into the terminal. The problem? That by the time I’m executing that anonymous function a no longer exists. Or does it?

The answer is it does, because when we return that new function, its scope is returned with it, so it can still access a wherever we call it from. That’s the key here.

The other peculiarity of closures is that they all share the same scope if they are created inside the same function. So if our function X would’ve created multiple functions, they all would’ve shared the same scope, thus they would all have had a reference to a . Now let’s take this concept a bit further:

Let’s quickly unpack this beauty. Our function X is:

  • Defining a very big string.
  • Getting the value of key X from the global state.
  • Defining fn1 (which never gets used BTW) where we use the value we got in the previous step.
  • We’re setting a new value for the state key X which references the big string we defined at the start of this function.

Remember what I said before? Closures shared scope, so both our logic method and our fn1 function share the same scope, and the latter is referencing the older value of the state key X . So every time we call this function, that value will get saved inside a new variable XState and while we will overwrited it when we call the set method at the end, that XState variable will remain active, because it’s used within a closure ( fn1 ). That big string will get duplicated in memory by the second time we call our function X , and if we keep calling it, it’ll keep duplicating it.

After a few calls, we’ll start seeing our free memory go down the drain. And don’t think for a minute that closures are a use case that you never run into. They’re more common than you’d think, it’s just that sometimes we don’t realize we’re using them.

Let me quickly rename a few things in my previous example:

Granted, the code will probably not work as-is on your React application, but you get the point. I’ve renamed a few things and suddenly we’re losing memory on a React component.

So be careful when you deal with functions and the variables they use.

What other practices have I left out of the list that you consider terrible for JavaScript developers? What’s the one you love to hate? Leave an example in your comments and let’s grow this list!

--

--

I write about technology, freelancing and more. Check out my FREE newsletter if you’re into Software Development: https://fernandodoglio.substack.com/