×
Just like we have seen design patterns, there are certain ways that we should strive to not write JavaScript. We call those anti-patterns. Let's examine some!
One of the most common anti-patterns in JavaScript is declaring lots of variables in the global scope. This is known as "polluting" the global scope (also called "namespace") and is not only an issue when working with other developers and scripts, but it can allow for memory leaks, which we will learn more about later!
eval
Another very common anti-pattern in JavaScript is using the eval
function. You will commonly hear that eval
is "evil" - but why? Since the eval
function will evaluate anything that is passed to it, using it opens yourself up to serious security threats if you are using eval
with any input that can be modified externally. There are also performance issues when using it as well. You can learn more about how it has been used here.
Another anti-pattern is modifying the Object prototype as it can have many unintended consequences, which you would not expect. Let's look at this code:
var obj = {name: "Elie", job: "Instructor"} for (var key in obj) { console.log(key); // name, job } Object.prototype.lastName = "Schoppik"; for (var key in obj) { console.log(key); // name, job, lastName }
Whoa! By modifying the Object.prototype
we can actually break our for in
loops when iterating over an object, so it's always best to avoid extending the Object.prototype
There are also anti-patterns when working with Promises, which you can read about here as well as additional anti-pattners in JavaScript.
While memory leaks are not exactly an 'anti-pattern', they are something you should always consider when writing code. Memory leaks occur when there is memory that is not required but still allocated. In JavaScript, memory is deallocated/de-referenced through a tool called "garbage collection." Garbage collection manages memory through constantly checking to see what variables are not in use by your application.
With lower level languages, specifically languages like C, the developer is in charge of managing memory and needs to manually allocate and free/deallocate memory. In JavaScript, we do not have to worry about manually managing memory, but we can still make mistakes which cause variables that are not being used to not be collected by the garbage collector. Without getting into too much detail, memory leaks occur when there are existing references which the garbage collector can not remove, even if the developer is done using that data. Let's look at some examples:
We previously mentioned that declaring variables in the global scope inside of functions was a bad idea because it's not readable and unecessary, but there is also another issue. We can very easily create variables in the global scope that are intended for use only in the function. Once the function has run that variable can not be garbage collected because it is in the global scope!
function badIdea(){ data = "This is no good!" } badIdea() // data can still be accessed!
The specific issue with timers is when a setInterval
is not cleared. If the setInterval
is not cleared, variables inside of the callback function can not be cleared as well. With DOM references it is a bit tricker. If you create a reference to a DOM node with a variable and then remove the element from the dom, you will still have a reference to the DOM variable in memory and it will not be able to be collected by the garbage collector. The last example of how memory leaks can occur is with closure. Remember that through closure we can access variables defined in outer functions that have returned so it is important to make sure that at some point your code dereferences these values or else they will not be able to be garbage collected.
Memory leaks may seem a bit obscure, but they can happen quite easily especially when using frameworks and libraries without understanding how they really work. You can read here about memory leaks with Angular 1 applications and how you can allow for memory leaks with React here. Even if you do not yet understand how these frameworks work, just know they do not "protect" you from memory leaks. While they may try to prevent as many memory leaks as possible, you as the developer are still responsible for handling these issues if they arise.
To help with some anti-patterns and to avoid memory leaks, we can enable strict mode, which throws errors instead of letting our code fail silently. To use strict mode we add the string "use strict". Here are some examples for how strict mode can help out.
Note - if you are using "use strict" in the Chrome console, make sure it is at the top of your code and that code run is right under it, it will not work if you just run "use strict" then execute different statements.
Strict mode stops us from declaring variables without a specified keyword (var
/ let
/ const
) and it sets the keyword this
to be undefined when inside of a function in the global context.
"use strict" noVarKeyword = "bad idea" // ReferenceError
"use strict" function badIdea(){ this.data = "done" } badIdea() // TypeError
Strict mode also prevents us from using delete
on things we should never ever delete. We will not actually delete the example below if we were not using strict mode, but it would be bad code to even write in the first place.
"use strict"; delete Array.prototype; // TypeError
Strict mode also lets us know if we are using incorrect parameters before a function is run. This prevents future ReferenceErrors by throwing SyntaxErrors before.
function sum(a, a, c) { // !!! syntax error "use strict"; return a + b + c; // wrong if this code ran }
Strict mode also prevents us from setting properties on primitive values, which we can do outside of strict mode (even though they do not actually get added)
false.awesome = "yes" false.awesome // undefined "use strict" false.awesome = "no" // TypeError
You can learn more about strict mode here.
You can read more about memory leaks and how to debug them with the Chrome dev tools here and here.