10 Mistakes That JavaScript Beginners Often Make
JavaScript is an easy language to get started with, but to achieve mastery takes a lot of effort. Beginners often make a few well-known mistakes that come back and bite them when they least expect. To find which these mistakes are, keep reading!
1. Missing curly braces
One practice, which JavaScript beginners are often guilty of, is omitting curly braces after statements like if
, else
, while
and for
. Although it is allowed, you should be extra careful, because this practice can often conceal problems and be the source of bugs. See the below example:
// Say hello to Gandalf hello('Gandalf'); function hello(name){ // This code doesn't do what the indentation implies! if(name === undefined) console.log('Please enter a username!'); fail(); // The following line is never reached: success(name); } function success(name){ console.log('Hello, ' + name + '!'); } function fail(){ throw new Error("Name is missing. Can't say hello!"); }
Although the fail()
call is indented and looks as if it belongs to the if
statement, it does not. It is always called. So it is a good practice to always surround blocks of code with curly braces, even if there is only one statement involved.
2. Missing semicolons
When JavaScript is parsed, there is a process known as automatic semicolon insertion. As the name suggests, the parser is happy to insert missing semicolons for you. The purpose of this feature is to make JavaScript more approachable and easier to write by beginners. However, you should always include semicolons, because there are dangers in omitting them. Here is an example:
// This code results in a type error. Adding semicolons will fix it. console.log('Welcome the fellowship!') ['Frodo', 'Gandalf', 'Legolas', 'Gimli'].forEach(function(name){ hello(name) }) function hello(name){ console.log('Hello, ' + name + '!') }
Because there is a semicolon missing on line 3, the parser assumes that the opening bracket on line 5 is an attempt to access a property using the array accessor syntax (see mistake #8), and not a separate array, which is not what was intended and results in a type error. The fix is simple - always write semicolons.
Some experienced JavaScript developers prefer to leave out semicolons, but they are perfectly aware of the bugs that this might cause and know how to prevent them.
3. Not understanding type coercion
JavaScript is dynamically typed. This means that you don't need to specify a type when declaring a new variable, and you can freely reassign or convert its value. This makes JavaScript much easier to write than something like C# or Java, but you open the doors for potential bugs that in other languages are caught during the compilation step. Here is an example:
// Listen for the input event on the textbox var textBox = document.querySelector('input'); textBox.addEventListener('input', function(){ // textBox.value holds a string. Adding 10 appends // the string '10', it doesn't perform an addition.. console.log(textBox.value + ' + 10 = ' + (textBox.value + 10)); });
<input type="number" placeholder="Enter a number here" />
The problem can be easily fixed by using parseInt(textBox.value, 10)
to turn the string into a number before adding 10 to it. Depending on how you use a variable, the runtime might decide that it should be converted in one type or another. This is known as type coercion. To prevent types from being implicitly converted when comparing variables in if
statements, you can use strict equality checks (===).
4. Forgetting var
Another practice that beginners are guilty of, is forgetting to use the var
keyword when declaring variables. JavaScript is very permissive, and the first time it sees that you've used a variable without the var
statement, it will silently declare it for you globally. This can be the source of subtle bugs. Here is an example, which also shows a different bug - missing a comma when declaring multiple variables at once:
var a = 1, b = 2, c = 3; function alphabet(str){ var a = 'A', b = 'B' // Oops, missing ',' here! c = 'C', d = 'D'; return str + ' ' + a + b + c + '…'; } console.log( alphabet("Let's say the alphabet!") ); // Oh no! Something went wrong! c has a new value! console.log(a, b, c);
When the parser reaches line 4, it will insert a semicolon automatically, and then interpret the c
and d
declarations on line 5 as global. This will cause the value of the outer c variable to change. Read about more JavaScript gotchas here.
5. Arithmetic operations with floats
This mistake is true for nearly every programming language out there, including JavaScript. Due to the way floating point numbers are represented in memory, arithmetic operations are not as precise as you'd think. Here is an example:
var a = 0.1, b = 0.2; // Surprise! this is false: console.log(a + b == 0.3); // Because 0.1 + 0.2 does not produce the number that you expect: console.log('0.1 + 0.2 = ', a + b);
To work around this problem, you should not use use decimals if you need absolute correctness - use whole numbers, or if you need to work with money, use a library like bignumber.js.
6. Using constructors over literals
When Java and C# programmers start writing JavaScript, they often prefer to create objects using constructors: new Array()
, new Object()
, new String()
. Although they are perfectly supported, it is recommended to use the literal notations: []
, {}
, ""
, because the constructor functions have subtle peculiarities:
/* Using array constructors is valid, but not recommended. Here is why. */ // Create an array with four elements: var elem4 = new Array(1,2,3,4); console.log('Four element array: ' + elem4.length); // Create an array with one element. It doesn't do what you think it does: var elem1 = new Array(23); console.log('One element array? ' + elem1.length); /* String objects also have their warts */ var str1 = new String('JavaScript'), str2 = "JavaScript"; // Strict equality breaks: console.log("Is str1 the same as str2?", str1 === str2);
The solution is simple: try to always use the literal notation. Besides, JS arrays don't need to know their length in advance.
7. Not understanding how scopes work
A difficult concept for beginners to understand is JavaScript's scoping rules and closures. And rightfully so:
// Print the numbers from 1 to 10, 100ms apart. Or not. for(var i = 0; i < 10; i++){ setTimeout(function(){ console.log(i+1); }, 100*i); } /* To fix the bug, wrap the code in a self-executing function expression: for(var i = 0; i < 10; i++){ (function(i){ setTimeout(function(){ console.log(i+1); }, 100*i); })(i); } */
Functions retain visibility to variables in their parent scopes. But because we are delaying the execution with a setTimeout
, when the time comes for the functions to actually run, the loop has already finished and the i
variable is incremented to 11.
The self executing function in the comment works, because it copies the i
variable by value and keeps a private copy for each timeout function. Read more about scopes here and here.
8. Using eval
Eval is evil. It is considered a bad practice, and most of the times when you use it, there is a better and faster approach.
/* Using eval to access properties dynamically */ var obj = { name: 'Foo Barski', age: 30, profession: 'Programmer' }; // Which property to access? var access = 'profession'; // This is a bad practice. Please don't do it: console.log( eval('obj.name + " is a " + obj.' + access) ); // Instead, use array notation to access properties dynamically: console.log( obj.name + " is a " + obj[access]); /* Using eval in setTimout */ // Also bad practice. It is slow and difficult to read and debug: setTimeout(' if(obj.age == 30) console.log("This is eval-ed code, " + obj[access] + "!");', 100); // This is better: setTimeout(function(){ if(obj.age == 30){ console.log('This code is not eval-ed, ' + obj[access] + '!'); } }, 100);
Code inside eval
is a string. Debug messages arising from eval blocks are incomprehensible and you have to juggle with escaping single and double quotes. Not to mention that it is slower than regular JavaScript. Don't use eval unless you know what you are doing.
9. Not understanding asynchronous code
Something that is unique to JavaScript is that nearly everything is asynchronous, and you need to pass callback functions in order to get notified of events. This doesn't come intuitively to beginners, and they quickly find themselves scratching their heads on a bug that is difficult to understand. Here is an example, in which I use the FreeGeoIP service to fetch your location by IP:
var userData = {}; // Fetch the location data for the current user. load(); // Output the location of the user. Oops, it doesn't work! Why? console.log('Hello! Your IP address is ' + userData.ip + ' and your country is ' + userData.country_name); // The load function will detect the current visitor's ip and location with ajax, using the // freegeoip service. It will place the returned data in the userData variable when it's ready. function load(){ $.getJSON('https://freegeoip.net/json/?callback=?', function(response){ userData = response; // Uncomment this line to see what is returned: // console.log(response); }); }
Even though the console.log
comes after the load()
function call, it is actually executed before the data is fetched.
10. Misusing event listeners
Let's say that you want to listen for clicks on a button, but only while a checkbox is checked. Here is how a beginner might do it (using jQuery):
var checkbox = $('input[type=checkbox]'), button = $('button'); // We want to listen for clicks only when the checkbox is marked. checkbox.on('change', function(){ // Is the checkbox checked? if(this.checked){ // Listen for clicks on the button. button.on('click', function(){ // This alert is called more than once. Why? alert('Hello!'); }); } });
<input type="checkbox" /> <button>Click me!</button> <p>Click the checkbox a few times.</p>
This is obviously wrong. Ideally, you should listen for an event only once, like we did with the checkbox's change event. Repeatedly calling button.on('click' ..)
results in multiple event listeners that are never removed. I will leave it as an exercise for the reader to make this example work :)
Conclusion
The best way to prevent mistakes like these from happening is to use JSHint. Some IDEs offer built-in integration with the tool, so your code is checked while you write. I hope that you found this list interesting. If you have any suggestions, bring them to the comment section!
Bootstrap Studio
The revolutionary web design tool for creating responsive websites and apps.
Learn more
Comments 25
There are of course few different ways to solve #10. Here is one example:
10
You can use $.trigger() also
Thanks!
Nice, I had to learn this on the hard way. This article is very helpful for begginers.
Thank you for the article!
What's the correct answer for #10?
Is it to use .on() and .off()?
Well, either check the current state of the checkbox when button is clicked or just unbind the event listener when checkbox is not checked: else { button.unbind(); }
Problem #4 is tricky. Even if I may consider myself experienced, it still happens to me from time to time. Especially when refactoring the code, switching lines etc. and not using "use strict". But usually I don't miss the comma, but there's a semicolon instead.
Ironically, it never happened to me when I was I beginner and I used var for every line of variable declarations.
I'd take the "beginner" off from the title. I have been in contact with Javascript for some time and still found this an interesting read, specially for the examples. Many people will probably skip this article since it is "for beginners". Maybe "common mistakes" instead of "beginner mistakes" would make for a more attractive title.
I want to say that point 5 it's not just for Javascript but for any other programming language that uses IEEE Standard for Floating-Point Arithmetic (IEEE 754). You could add variable and function hoisting instead.
I wish I had read this a few years ago. Great post!
Very nice article. It is all true, I suffered most of these while learning JavaScript.
For 9 are there any good rule of thumb to stick with?
What about example 9, does anyone know how to solve it? How to wait/check that we got a response from the server before trying to print or use the fetched data?
Promises are a convenient way to deal with these asynchronous processings.
Check implementation such as jQuery Deferred.
Just move the first console.log to replace the console.log(response) in the function and it will work.
So accurate!
And I like the JS code editor that can switch between Edit and Run modes.
It's really good for webapps like jsfiddle.net and codepen.io
The most common ones are missing curly braces in statements and missing semicolons. These are general mistakes that almost every beginner omits.
I'm a beginner trying to figure out the example 3. Could somebody help with the correct response to perform an addition?
Hi Joni
You should replace this line.
console.log(textBox.value + ' + 10 = ' + (parseInt(textBox.value, 10) + 10));
Cheers
Hey Jan,
Thank you for your help :)
Cheers!
There are several seasoned JS developers on the planet that write JavaScript without semicolons.
Once a developer understands how ASI works it's a valid choice to not use semicolons behind every line.
Novice devs are often afraid of ASI because they don't know how it works. But it's actually quite simple to remember. Plus, sane Javascript code and ASI go together pretty well.
Isaac Schlueter wrote a pretty decent round up:
http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding
So for a non-beginner developer it should be quite easy to read/write ASI or non-ASI Javascript.
There are many more reasons not to write JavaScript without semicolons. The person coming after you will have to, in many cases, read your mind to be sure of what your intentions were. If you are the sole developer, feel free to skip the semicolons...
Great post. If you want to cover the most common issues, check this one out as well.
https://www.ziptask.com/Most-Common-JavaScript-Mistakes-of-All-Time
Maybe it's mostly jQuery, but my TOP #1 mistage is allways selectors.
9 out of 10 debugs, i have forgot .class #id or any other selector when writing my code.
Thanks for a great post.