DEV Community

John Au-Yeung
John Au-Yeung

Posted on • Updated on

Common JavaScript Mistakes You May be Making

Subscribe to my email list now at http://jauyeung.net/subscribe/

Follow me on Twitter at https://twitter.com/AuMayeung

Many more articles at https://medium.com/@hohanga

JavaScript is a language that’s friendlier than many other programming languages in the world. However, it’s still very easy to make mistakes when writing JavaScript code through misunderstanding or overlooking stuff that we already know. By avoiding some of the mistakes below, we can make our lives easier by preventing bugs and typos in our code that bog us down with unexpected results.


Trying to Overload Functions

Functional overloading is a feature of some programming languages where you can declare functions with the same name but different signatures. In JavaScript, we can’t overload functions. Whenever a function is declared more than once, the one that is declared later overwrites the one that’s declared earlier. This is because functions are objects and declaring a function is like assigning an object to a variable. When you assign an object to a variable more than once, then the value that’s assigned later will overwrite the value that’s assigned earlier. That means that we can’t have two functions with the same name in the same module in JavaScript. For example, if we have the following,

function add(a, b, c) {  
  return a + b + c;  
}

function add(a, b) {  
  return a + b;  
}

console.log(add(1, 2, 3));

then we get three because the add function that’s declared later has overwritten the one that’s declared earlier. To fix this, we have to rename one of them. We can also put them inside two different objects. Then they can have the same name since they aren’t in the same level. Also, we can write an Immediately Invoked Function Expression, or IIFE for short. IIFEs are run as soon as they’re defined. To wrap them in an object, we can write the following:

const a = {  
  add(a, b, c) {  
    return a + b + c;  
  }  
}

const b = {  
  add(a, b) {  
    return a + b;  
  }  
}

console.log(a.add(1, 2, 3));  
console.log(b.add(1, 2, 3));

As we can see, if we run the code, then the console.log of the first one will be six and the second one will be three since a.add has three parameters and b.add has two.

We can also use an IIFE as in the following example:

const sum1 = (function add(a, b, c) {  
  return a + b + c;  
})(1, 2, 3);

const sum2 = (function add(a, b) {  
  return a + b;  
})(1, 2, 3);

console.log(sum1);  
console.log(sum2);

In the code above, we wrapped the function inside the parentheses and then called it immediately after it was defined. Then we assigned the returned result to a variable. After that, we get six and three as we wanted. Because we called each function immediately and returned the result, we get the right result since they didn’t overlap. It also means that they can’t be called again.


Missing Parameters

When we add a new parameter to a function, then we have to remember to pass in the extra argument in the function calls. Otherwise, there may be undefined errors. To avoid undefined parameters creating errors, we can either check for it in our function, or we can set a default value of the parameter. For example, if we have the following function

function addressFn(address, city, region) { ... }

and we want to add a countryparameter and we have other parts of our program calling the function above, then we can add a default parameter. We can do this by writing the following:

function addressFn(address, city, region, country = 'US') { ... }

This way, if the country argument didn’t get passed in, country will be set to 'US'.


Forgetting About the this Keyword

When we try to access some property from another property inside an object, we should use the this keyword to get the property’s value that we want. For example, if we have the following,

let obj = {  
  prop: "some text",  
  method() {  
    console.log(prop);  
  }  
};

obj.method();

we will get an Uncaught ReferenceError: prop is not defined error when we run the code above. This is because we forgot to put the this keyword before the prop variable. Instead, we need to write the following:

let obj = {  
  prop: "some text",  
  method() {  
    console.log(this.prop);  
  }  
};

obj.method();

When we run the code above, then we get 'some text', which is what we wanted.


Iterate Through the Object Keys

The for...in loop will loop through the keys of the current object as well as all the prototypes’ keys. This isn’t ideal for all situations. It’s also slower than the other ways of iterating through the keys of an object. With the for...in loop, we need to use the Object.hasOwnProperty function to check that the property is originally defined in the object. This makes the loop even slower. This is a problem if we have a large object with lots of properties. For example, if we have,

const parent = {  
  pa: 1,  
  pb: 2  
}  
let obj = Object.create(parent);
obj.a = 1;  
obj.b = 2;  
obj.c = 3;  
obj.d = 4;  
obj.e = 5;

then the for...in loop will loop through all the properties of the parent and the properties added to obj. If we only want to loop through the properties in obj, then we have to loop using the hasOwnProperty function as in the following code:

for (const key in obj) {  
  if (obj.hasOwnProperty(key)) {  
    console.log(obj[key]);  
  }  
}

However, this is slower than the newer alternatives, which are Object.keys to get the keys of an object and Object.entries to get the key-value pairs of an object. Then we loop through them with the for...of loop since both return arrays. They only loop through the object’s properties and nothing up the prototype chain. The fastest ways to loop through the entries are these two functions. We can use them as follows:

const parent = {  
  pa: 1,  
  pb: 2  
}  
let obj = Object.create(parent);obj.a = 1;  
obj.b = 2;  
obj.c = 3;  
obj.d = 4;  
obj.e = 5;

for (const key of Object.keys(obj)) {  
  console.log(obj[key]);  
}

for (const [key, value] of Object.entries(obj)) {  
  console.log(value);  
}

In each example, we get the following logged,

a 1  
b 2  
c 3  
d 4  
e 5

which means that we’re getting what we want from the Object.keys and Object.entries functions.


Even though JavaScript is a friendly language, it’s still very easy to make mistakes when writing JavaScript code. Remember that in JavaScript, we can’t overload functions, so we can’t define two functions with the same name in the same level. If there’s potential for a function parameter to not be set, then we can set a default parameter so that it will never be undefined. Also, we can’t forget about the this object when we’re accessing one property from another property of the same object. Finally, we shouldn’t use the for...in loop anymore to loop through the keys of an object because it’s slow and clunky if we just want to loop through the keys of the current object without its prototype’s keys. We want to use the Object.keys or Object.entries functions instead so we get the keys or the key-value pairs, respectively, as arrays, and we can loop through them like any other array.

Top comments (6)

Collapse
 
mrdreix profile image
Vladimír Gál

Wouldn't be a better approach instead of doing

for(const key of Object.keys(obj)) {  
  console.log(obj[key]);  
}

do this ?

Object.keys(obj).map(item => console.log(item));
Collapse
 
aminnairi profile image
Amin • Edited

Map would be useful if you did use the return value for each mapped values.

In this case, the console.log method won't return anything (or return undefined). So you would rather use something like Array.prototype.forEach.

Object.keys(obj).forEach(item => console.log(item));

As you can see, we are repeating the item keyword a lot. We can pass the console.log as a parameter to our Array.prototype.forEach which is a higher order function like so.

Object.keys(obj).forEach(console.log);

Which would be the equivalent syntax to what we wrote.

Collapse
 
craigmc08 profile image
Craig McIlwrath

The second simplification is called eta-reduction, but in this case it is not valid eta-reduction because result of the expression changes.

In the original, it will log the value of each item. In the reduced form, it logs the value, index, and entire array for each item (Array.prototype.forEach actually passes 3 parameters). This is a mistake to be mindful of.

Thread Thread
 
aumayeung profile image
John Au-Yeung

Yea. That example works in JavaScript because it lets use pass in anything to a function.

It's very easy to make a mistake passing in a function like that

Collapse
 
mrdreix profile image
Vladimír Gál

Even better. Thanks :)

Collapse
 
aumayeung profile image
John Au-Yeung

The last snippet assume that everyone knows what the first argument of console.log should be.

So it's leas clear. But both are fine if we're just logging the value straight up.