The company
Rollbar , which is creating tools for working with bugs in the programs, decided to analyze the database of more than 1000 projects on JavaScript and find the errors that are most common. As a result, they formed a list of the 10 most common mistakes, analyzed the reasons for their occurrence and told how to correct and avoid them. They believe that getting to know these bugs will help JS developers write better code.
Today we publish a translation of their research.
Analysis Method
Nowadays, data is everything, so we found, analyzed, and ranked the errors that are most often found in
JavaScript projects . Namely, error information was collected for each project, after which the number of errors of each type was calculated. Errors were grouped by their checksum, the calculation method of which can be found
here . With this approach, if, for example, in one project a certain error is found, which is then found somewhere else, such errors are grouped. This allows, after analyzing all the projects involved in the study, to get a brief summary of the errors, rather than something like a huge log file, which is inconvenient to work with.
The study focused on the most common errors. In order to select such mistakes, they were ranked by the number of projects of different companies in which they are found. If only the total number of occurrences of some kind of error were included in this rating, then errors typical of some very large project, but rarely found in other projects, would distort the results.
')
Here are ten mistakes that were selected based on the results of the study. They are sorted by the number of projects in which they are found.
Errors that occur in JS projects most oftenError names are an abbreviated version of the error message that the system generates. Relying on system messages allows you to easily identify errors when they occur. Now we will analyze each of them, tell you about what causes them, and how to deal with them.
1. Uncaught TypeError: Cannot read property
If you write programs in JavaScript, then you probably encountered this error more often than you would like. A similar error occurs, for example, in Google Chrome when you try to read a property or call a method of an undefined variable, that is, one that has the value
undefined
. You can see this error in action using the Chrome Developer Tools Console.
Error Cannot read propertyThis error can occur for many reasons, but most often it is caused by an incorrect initialization of the state when rendering the user interface element. Take a look at an example of how this can happen in a real application. Here we use React, but the same initialization error is typical for Angular, Vue, and for any other frameworks.
class Quiz extends Component { componentWillMount() { axios.get('/thedata').then(res => { this.setState({items: res.data}); }); } render() { return ( <ul> {this.state.items.map(item => <li key={item.id}>{item.name}</li> )} </ul> ); } }
Here we must pay attention to two important things:
- At the very beginning, the state of the component (that is,
this.state
) is represented by the value undefined
.
- When asynchronous data is loaded, the component will be displayed at least once before the data is loaded, regardless of whether this is done in
componentWillMount
or componentDidMount
. When the Quiz
element is displayed for the first time, this.state.items
is undefined
. This, in turn, means that itemList
receives items that are also represented by the value undefined
. As a result, we see the following error in the console: "Uncaught TypeError: Cannot read property 'map' of undefined"
.
This error is easy to fix. The easiest way is to initialize the state in the constructor with appropriate default values.
class Quiz extends Component {
Your application code will look different, but we hope that now you know how to correct this error in your project and how to avoid its appearance. If what was discussed is not suitable for you, perhaps the analysis of the following errors will help you.
2. TypeError: 'undefined' is not an object (evaluating ...
This error occurs in the Safari browser when you try to read a property or call an undefined object method. You can look at this error using the Safari Developer Tools Console. In fact, here we have the same problem that we have analyzed above for Chrome, but in Safari it leads to another error message.
Error 'undefined' is not an objectCorrect this error should be the same as in the previous example.
3. TypeError: null is not an object (evaluating
This error occurs in Safari when you try to access a method or property of a variable represented by a
null
value. Here’s how it looks in the Safari Developer Console.
TypeError Error: null is not an objectRecall that in JavaScript,
null
and
undefined
are not the same thing, which is why we see different error messages. The meaning of the
undefined
value written to a variable indicates that no value has been assigned to the variable, and
null
indicates an empty value. In order to make sure that
null
not equal to
undefined
, you can compare them using the strict equality operator:
Comparing undefined and null with non-strict and strict equality operatorsOne of the reasons for this error in real-world applications is to try to use the DOM element in JavaScript before loading the element. This happens because the DOM API returns
null
for references to empty objects.
Any JS code that works with DOM elements must be executed after creating DOM elements. JS-code is interpreted from top to bottom as it appears in the HTML document. Therefore, if the
<script>
with the program turns out to be in front of code describing DOM elements, the program will be executed during the page parsing before it is completed. This error will occur if the DOM element accessed from the script was not created before loading the script.
In the following example, we can fix the problem by adding an event listener to the code that will alert us that the page is fully loaded. After triggering an event handler added with
addEventListener
, the
init()
method will be able to work correctly with DOM elements.
<script> function init() { var myButton = document.getElementById("myButton"); var myTextfield = document.getElementById("myTextfield"); myButton.onclick = function() { var userName = myTextfield.value; } } document.addEventListener('readystatechange', function() { if (document.readyState === "complete") { init(); } }); </script> <form> <input type="text" id="myTextfield" placeholder="Type your name" /> <input type="button" id="myButton" value="Go" /> </form>
4. (unknown): Script error
This error occurs when an uncaught JavaScript error crosses domain boundaries when a cross-domain constraint policy is violated. For example, if your JS code is placed on a CDN resource, the message about any uncaught error (that is, an error that was not intercepted in the
try-catch
and reached the
window.onerror
handler) will indicate a
Script error
, rather than a useful one. for the purpose of eliminating this error information. This is one of the browser security mechanisms aimed at preventing data transmission between code fragments, which originate from different domains, and which under normal conditions are forbidden to exchange information.
Here is a sequence of actions that will help you see this error.
1. Sending an
Access-Control-Allow-Origin
header.
Setting the
Access-Control-Allow-Origin
header to
*
indicates that the resource can be accessed from any domain.
The star sign can be replaced with a specific domain, if necessary, for example
: Access-Control-Allow-Origin: www.example.com
. However, support for multiple domains is quite complicated. Such support may not be worth the effort to ensure it, if you are using a CDN, due to possible caching problems. Details on this can be found
here .
Here are some examples of installing this header in various environments.
ApacheIn the folder from which your JavaScript files will be loaded, create a
.htaccess
file with the following contents:
Header add Access-Control-Allow-Origin "*"
NginxAdd the directive
add_header
to the
location
block, which is responsible for maintaining your JS files:
location ~ ^/assets/ { add_header Access-Control-Allow-Origin *; }
HaproxyAdd the following setting to the system parameters responsible for supporting JS files:
rspadd Access-Control-Allow-Origin:\ *
2. Set
crossorigin="anonymous"
in the
<script>
.
In your HTML file for each of the scripts for which the
Access-Control-Allow-Origin
crossorigin="anonymous"
set, set
crossorigin="anonymous"
in the
<script>
. Before adding the
crossorigin
property to the
<script>
check that the header for the script file is sent. In Firefox, if the
crossorigin
attribute
crossorigin
present, and the
Access-Control-Allow-Origin
header is not, the script will not be executed.
5. TypeError: Object does not support property
This error occurs in IE when trying to call an undefined method. You can see this error in the IE developer console.
Error Object does not support propertyThis error is equivalent to the error
"TypeError: 'undefined' is not a function"
that occurs in Chrome. We draw your attention to the fact that we are talking about the same logical error, which different browsers report differently.
This is a common IE problem in web applications that take advantage of JavaScript namespace capabilities. When this error occurs, in 99.9% of cases its cause is the inability of IE to bind methods located in the current namespace to the keyword
this
. For example, suppose there is a
Rollbar
object with the
isAwesome
method. Usually, being within this object, the
isAwesome
method can be called like this:
this.isAwesome();
Chrome, Firefox and Opera will normally accept such a command. IE will not understand it. Thus, when using such constructions, it is best to always prefix the name of the method with the name of the object (namespace) in which it is defined:
Rollbar.isAwesome();
6. TypeError: 'undefined' is not a function
This error occurs in Chrome when trying to call an undefined function. You can look at this error in the Chrome Developer Tools console and in a similar Firefox console.
TypeError Error: 'undefined' is not a functionSince JavaScript programming approaches and design patterns are constantly becoming more complex, there is also a corresponding increase in the number of situations in which, within the callback functions and closures, scopes appear that use references to their own methods and properties using the keyword
this
, is a fairly common source of confusion and error.
Consider the following example:
function testFunction() { this.clearLocalStorage(); this.timer = setTimeout(function() { this.clearBoard();
Executing the above code will result in the following error:
"Uncaught TypeError: undefined is not a function."
The reason for this error is that when we call
setTimeout()
we actually call
window.setTimeout()
. As a result, the anonymous function that is passed to
setTimeout()
is found to be in the context of the
window
object, which does not have a
clearBoard()
method.
The traditional approach to solving this problem, compatible with older versions of browsers, is to simply save the reference to
this
in some variable, which can then be accessed from the closure. For example, it might look like this:
function testFunction () { this.clearLocalStorage(); var self = this;
In more modern browsers, you can use the
bind()
method to pass the required reference:
function testFunction () { this.clearLocalStorage(); this.timer = setTimeout(this.reset.bind(this), 0);
7. Uncaught RangeError: Maximum call stack
There are several reasons for this error, for example, in Chrome. One of them is an endless call to a recursive function. This is what the error looks like in the Chrome developer console:
Error Maximum call stack size exceededSimilar can occur in the case when functions pass a value that is outside of some acceptable range of values. Many functions take only numbers that are in a certain range. For example, the
Number.toExponential(digits)
and
Number.toFixed(digits)
functions take
digits
, represented by a number from 0 to 20, and the
Number.toPrecision(digits)
function takes numbers from 1 to 21. Take a look at situations in which calling these and some other functions lead to errors:
var a = new Array(4294967295); //OK var b = new Array(-1); // ! var num = 2.555555; document.writeln(num.toExponential(4)); //OK document.writeln(num.toExponential(-2)); //! num = 2.9999; document.writeln(num.toFixed(2)); //OK document.writeln(num.toFixed(25)); // ! num = 2.3456; document.writeln(num.toPrecision(1)); //OK document.writeln(num.toPrecision(22)); // !
8. TypeError: Cannot read property 'length'
This error occurs in Chrome when you try to read the
length
property of a variable to which
undefined
written. Take a look at this error in the Chrome developer tools console.
Error Cannot read property 'length'Usually, referring to the
length
property, we find out the length of the arrays, but the error described above can occur if the array is not initialized, or if the variable name is hidden in the scope inaccessible from the place where this variable is attempted to be accessed. In order to better understand the nature of this error, consider the following example:
var testArray= ["Test"]; function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction();
When you declare a function with parameters, these parameters become local variables for it. In our example, this means that even if there is a
testArray
variable in the scope that surrounds the function, a parameter with the same name will hide this variable and will be perceived as a local variable of the function.
In order to solve this problem, in our case, you can go one of the following two ways:
- Removing the parameter specified when declaring a function (as you can see from the example, we want to work with the function with an array that is declared outside of its limits, so you can do without the function parameter here):
var testArray = ["Test"]; function testFunction(/* */) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction();
- Function call with passing it the previously declared array:
var testArray = ["Test"]; function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction(testArray);
9. Uncaught TypeError: Cannot set property
When we try to access an undefined variable, we actually work with a value of type
undefined
, and this type does not support reading or writing properties. In this case, the application will give the following error:
"Uncaught TypeError cannot set property of undefined."
Take a look at it in the Chrome browser.
Error Cannot set propertyIf the
test
object does not exist, the error
"Uncaught TypeError cannot set property of undefined."
10. ReferenceError: event is not defined
This error occurs when trying to access an undefined variable, or a variable that is outside the current scope. Take a look at it in the Chrome console:
ReferenceError Error: foo is not definedIf you encounter this error when using an event-handling system, make sure that you are working with an event object passed as a parameter. Older browsers, like IE, offer global access to events, but this is not the case for all browsers. Libraries like jQuery are trying to fix this situation. In any case, it is recommended to use exactly the event object that is passed to the event handling function.
function myFunction(event) { event = event.which || event.keyCode; if(event.keyCode===13){ alert(event.keyCode); } }
Results
We hope you have learned something new from our error story that will help you avoid mistakes in the future, or maybe it has already helped to find an answer to a question that has long given you no peace.
Dear readers! What JS errors did you encounter in production?
