Categories
JavaScript Mistakes

Common JavaScript Mistakes — Part 3

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'.

Photo by Kendall Lane on Unsplash


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.

Categories
JavaScript Mistakes

More JavaScript Mistakes You May be Making

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.

Confusing Undefined and Null

JavaScript has both undefined and null for non-values. However, there are quite a few differences between the two. undefined means that the variable may have been declared, but nothing is set to it. A variable can also be explicitly set as undefined. The type of an undefined variable, when checking the type with the typeof operator, will get us the type 'undefined'. Functions that don’t return anything returns undefined. On the other hand, null values have to be explicitly set by functions that return null or just set directly to a variable. When we check an object that has the null value set, we’ll find that the type of it is'object' if a variable has the null value.

For this reason, it’s probably easier to stick to undefined whenever we can when we’re setting variable values to non-values. It reduces confusion and we only have to check that the type of a variable is 'undefined' to see whether it’s undefined. That’s less painful than having two checks, for both null and undefined.

To write functions that return undefined, we don’t have to do anything like the following example:

const f = () => {}

To set a variable that was assigned some other value to undefined, we can write:

x = undefined;

To check if a property value is undefine, we can write:

typeof obj.prop === 'undefined'

or

obj.prop === undefined

To check if a variable is undefined, we can write the following code:

typeof x === 'undefined'

A declared variable that hasn’t been assigned anything automatically has the value undefined.

If we have to check for null, then we can write:

obj.prop === null

or

x === null

for variables. We can’t use the typeof operator for checking null because the data type of null is 'object'.

Confusing Addition and Concatenation

In JavaScript, the + operator is used for both adding two numbers and concatenating strings together. Because JavaScript is a dynamic language, the operands are all automatically converted to the same type before the operation is applied. For example, if we have:

let x = 1 + 1;

then we get two because they’re both numbers. The + operation was used for addition like we expected. However, if we have the following expression:

let x = 1 + '1';

then we get '11' because the first operand was coerced into a string before the + operation is applied. The + operation was used for concatenation instead of addition. When we use the + operator on multiple variables, this makes knowing the type even harder. For example, if we have:

let x = 1;  
let y = 2;  
let z = x + y;

as we expect, we get three because x and y are both numbers. On the other hand, if we have:

let x = 1;  
let y = '2';  
let z = x + y;

then we get '12' because y is a string, so the + operator was used for concatenation instead. To solve this issue, we should convert all the operands to numbers first before using them with the + operator. For example, we should rewrite the above example into the following:

let x = 1;  
let y = '2';  
let z = Number(x) + Number(y);

The code above will get us 3 as the value of z since we converted them both to numbers with the Number factory function first. The Number function takes in any object and returns a number if it can be parsed into a number, or NaN otherwise. An alternative way to do this is to use the new Number(...).valueOf() function, as we do in the following code:

let x = 1;  
let y = '2';  
let z = new Number(x).valueOf() + new Number(y).valueOf();

Since new Number(...) is a constructor that creates an object type, we want to use the valueOf function to convert it back to a primitive type to make sure that what we get is a number type. A shorter way to do this is to write:

let x = 1;  
let y = '2';  
let z = +x + +y;

The + sign in front of a single operand will try to convert the single operand into a number or toNaN if it can’t be converted into a number. It does the same thing as the Number function. We can also convert a variable to a particular type of number. The Number object has a parseInt function to convert a string or object into an integer and a parseFloat function to convert a string or object into a floating-point number. parseInt takes the object you want to convert to a number as the first argument. It also takes a radix as an optional second argument, which is the base of the mathematical numeral systems. If the string starts with 0x, then the radix will be set to 16. If the string starts with anything else, then the radix will be set to 10.

We can use them as in the following examples:

let x = 1;  
let y = '2';  
let z = Number.parseInt(x) + Number.parseInt(y)

Also, we can use the parseFloat function as in the following code:

let x = 1;  
let y = '2';  
let z = Number.parseFloat(x) + Number.parseFloat(y)

We will get 3 in both of the examples above.

Breaking Return Statements Into Multiple Lines

JavaScript closes a statement at the end, so one line code is considered distinct from the other. For example, if we have:

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

we get undefined if we run console.log(add(1, 2)); since we ran the return statement, which ended the function execution, before a + b is run. Therefore, a + b will never be run in this function. To fix this, we either have to put the return statement all in one line or use parentheses to surround what we want to return. For example, we can write:

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

This will log 3 if we run console.log(add(1, 2)); since we are actually returning the computed result in the function. We can also write:

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

This is handy for returning expressions that might be longer than one line. This will also log 3 if we run console.log(add(1, 2));. For arrow functions, we can also write:

const add = (a, b) => a + b

for single-line functions since the return statement is implicit for single-line arrow functions. We can also write:

const add = (a, b) => (a + b)

to get the same thing. This also works for single-line arrow functions.

In JavaScript, if a statement is incomplete, like the first line of:

const power = (a) => {  
  const  
    power = 10;  
  return a ** 10;  
}

inside the function then the JavaScript interpreter will run the first line together with the second line to get the full statement. So:

const  
  power = 10;

is the same as:

const power = 10;

However, for complete statements like return statements, the JavaScript interpreter will treat them as separate lines. So:

return   
  a ** 10;

is not the same as:

return a ** 10;

Even though JavaScript is a friendly language, it’s still very easy to make mistakes when writing JavaScript code. It’s easy to confuse undefined and null when we aren’t familiar with JavaScript. Because of the dynamic typing nature of JavaScript, operators like the + operator that can do multiple things can easily be converted to a type we don’t expect and produce the wrong result. Also, if statements can be complete on their own, then they shouldn’t be written in their own lines if we want both lines to be together.

Categories
JavaScript Mistakes

JavaScript Mistakes You May be Making

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.


Confusing Undefined and Null

JavaScript has both undefined and null for non-values. However, there are quite a few differences between the two. undefined means that the variable may have been declared, but nothing is set to it. A variable can also be explicitly set as undefined. The type of an undefined variable, when checking the type with the typeof operator, will get us the type 'undefined'. Functions that don’t return anything returns undefined. On the other hand, null values have to be explicitly set by functions that return null or just set directly to a variable. When we check an object that has the null value set, we’ll find that the type of it is'object' if a variable has the null value.

For this reason, it’s probably easier to stick to undefined whenever we can when we’re setting variable values to non-values. It reduces confusion and we only have to check that the type of a variable is 'undefined' to see whether it’s undefined. That’s less painful than having two checks, for both null and undefined.

To write functions that return undefined, we don’t have to do anything like the following example:

const f = () => {}

To set a variable that was assigned some other value to undefined, we can write:

x = undefined;

To check if a property value is undefine, we can write:

typeof obj.prop === 'undefined'

or

obj.prop === undefined

To check if a variable is undefined, we can write the following code:

typeof x === 'undefined'

A declared variable that hasn’t been assigned anything automatically has the value undefined.

If we have to check for null, then we can write:

obj.prop === null

or

x === null

for variables. We can’t use the typeof operator for checking null because the data type of null is 'object'.

<img class="do t u hm ak" src="https://miro.medium.com/max/10084/0*79d0pk1RCiMWq6dC" width="5042" height="3151" role="presentation"/>

Photo by Mikhail Vasilyev on Unsplash


Confusing Addition and Concatenation

In JavaScript, the + operator is used for both adding two numbers and concatenating strings together. Because JavaScript is a dynamic language, the operands are all automatically converted to the same type before the operation is applied. For example, if we have:

let x = 1 + 1;

then we get two because they’re both numbers. The + operation was used for addition like we expected. However, if we have the following expression:

let x = 1 + '1';

then we get '11' because the first operand was coerced into a string before the + operation is applied. The + operation was used for concatenation instead of addition. When we use the + operator on multiple variables, this makes knowing the type even harder. For example, if we have:

let x = 1;  
let y = 2;  
let z = x + y;

as we expect, we get three because x and y are both numbers. On the other hand, if we have:

let x = 1;  
let y = '2';  
let z = x + y;

then we get '12' because y is a string, so the + operator was used for concatenation instead. To solve this issue, we should convert all the operands to numbers first before using them with the + operator. For example, we should rewrite the above example into the following:

let x = 1;  
let y = '2';  
let z = Number(x) + Number(y);

The code above will get us 3 as the value of z since we converted them both to numbers with the Number factory function first. The Number function takes in any object and returns a number if it can be parsed into a number, or NaN otherwise. An alternative way to do this is to use the new Number(...).valueOf() function, as we do in the following code:

let x = 1;  
let y = '2';  
let z = new Number(x).valueOf() + new Number(y).valueOf();

Since new Number(...) is a constructor that creates an object type, we want to use the valueOf function to convert it back to a primitive type to make sure that what we get is a number type. A shorter way to do this is to write:

let x = 1;  
let y = '2';  
let z = +x + +y;

The + sign in front of a single operand will try to convert the single operand into a number or toNaN if it can’t be converted into a number. It does the same thing as the Number function. We can also convert a variable to a particular type of number. The Number object has a parseInt function to convert a string or object into an integer and a parseFloat function to convert a string or object into a floating-point number. parseInt takes the object you want to convert to a number as the first argument. It also takes a radix as an optional second argument, which is the base of the mathematical numeral systems. If the string starts with 0x, then the radix will be set to 16. If the string starts with anything else, then the radix will be set to 10.

We can use them as in the following examples:

let x = 1;  
let y = '2';  
let z = Number.parseInt(x) + Number.parseInt(y)

Also, we can use the parseFloat function as in the following code:

let x = 1;  
let y = '2';  
let z = Number.parseFloat(x) + Number.parseFloat(y)

We will get 3 in both of the examples above.


Breaking Return Statements Into Multiple Lines

JavaScript closes a statement at the end, so one line code is considered distinct from the other. For example, if we have:

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

we get undefined if we run console.log(add(1, 2)); since we ran the return statement, which ended the function execution, before a + b is run. Therefore, a + b will never be run in this function. To fix this, we either have to put the return statement all in one line or use parentheses to surround what we want to return. For example, we can write:

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

This will log 3 if we run console.log(add(1, 2)); since we are actually returning the computed result in the function. We can also write:

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

This is handy for returning expressions that might be longer than one line. This will also log 3 if we run console.log(add(1, 2));. For arrow functions, we can also write:

const add = (a, b) => a + b

for single-line functions since the return statement is implicit for single-line arrow functions. We can also write:

const add = (a, b) => (a + b)

to get the same thing. This also works for single-line arrow functions.

In JavaScript, if a statement is incomplete, like the first line of:

const power = (a) => {  
  const  
    power = 10;  
  return a ** 10;  
}

inside the function then the JavaScript interpreter will run the first line together with the second line to get the full statement. So:

const  
  power = 10;

is the same as:

const power = 10;

However, for complete statements like return statements, the JavaScript interpreter will treat them as separate lines. So:

return   
  a ** 10;

is not the same as:

return a ** 10;

Even though JavaScript is a friendly language, it’s still very easy to make mistakes when writing JavaScript code. It’s easy to confuse undefined and null when we aren’t familiar with JavaScript. Because of the dynamic typing nature of JavaScript, operators like the + operator that can do multiple things can easily be converted to a type we don’t expect and produce the wrong result. Also, if statements can be complete on their own, then they shouldn’t be written in their own lines if we want both lines to be together.

Categories
JavaScript Mistakes

Common JavaScript Mistakes — Part 1

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.


Mismatching Brackets

Statements and functions nested in each other means that there’re multiple levels of brackets in each file. Usually, apps are pretty complex, so the levels can add up. This means that mismatching brackets is easy if you use a text editor that doesn’t support syntax highlight or don’t check for mismatched brackets. This can easily be avoided with modern text editors such as Visual Studio Code, Atom, and Sublime. If we want to use simpler text editors, then use linters and code formatting tools like ESLint and Prettier to detect these issues. They also let us format code automatically and detect common style issues that may occur, like quote style inconsistencies, number of characters in a line, functions that can be shortened, etc.


Mismatching Quotes and Parentheses

JavaScript lets us use single quotes, double quotes, and backticks for strings. They are equivalent. However, we should open and close with the same character. So if we open a string with a single quote, then use a single quote to close a string. If we start with double quotes or backticks, then use those respectively to close the string. Also, special characters like quotation marks have to escape to be included in a string in some cases. If you open a string with single quotes and you also use single quotes in a string, then you have to escape that to include them in the string. This also applies to double quotes and backticks. If you use double quotes in a double-quoted string, then you have to escape it. And if you use a backtick in a template string, then you have to escape the backtick character.

In if statements, parentheses always have to wrap around the whole condition. For example, something like

if (x > y) && (y < 10) {...}

won’t work. The correct way to write that statement is to write is

if ((x > y) && (y < 10)) {...}

if we want to check that both conditions are true.

We can easily avoid this by using a JavaScript code-aware text editor, like Visual Studio code, which will highlight these syntax errors for us so that we can fix these mistakes and make the code run.


Confusing the =, ==, and === Operators

The single equal (=) operator is for assigning the data on the right side to the variable on the left side. It should not be confused with the double equals (==) and the triple equals operator (===), which are operators for comparing values of the left and the right of the operator. In JavaScript we can use all three operators in an if statement. However, in most cases, we don’t mean to use the single equal operator inside the condition of the if statement. What we actually want is to use the double or triple equals operator to compare the operands on the left and right of it.

For example, we shouldn’t be writing

if (x = 1){ ... }

since we don’t want to assign 1 to x inside the if statement. Instead, we should be using the double or triple equals operator like the code below

if (x == 1){ ... }

or

if (x === 1){ ... }

The first example, if (x = 1){ … }, is always true, since x is truthy because it’s assigned the value of 1, which is truthy. What we actually want is to make a comparison which makes use of the conditional if statement.


Using Variables Outside of Block Level

If we use the var keyword to declare a variable, it can be referenced in any place below the var expression. For example, suppose we have:

for (var j = 0; j < 10; j++) {  
  j = j + 1;  
}  
console.log(j);

Then we can see that j is 10 when we run the console.log statement on the last line of the code. This is the reason that we have the let and const keywords to declare variables and constants. They’re blocked scoped so that they can’t be referenced outside the block. This means that we avoid bugs that can occur when we use var since the variable can’t be accessed outside the block that it’s declared in. As a result, we can’t assign it to anything else accidentally, causing issues with our code. Instead of using var like we did in the example above, we can use let like in the following example:

for (let j = 0; j < 10; j++) {  
  j = j + 1;  
}  
console.log(j);

We should get ReferenceError: j is not defined when we run the code above, which is a good sign since we don’t want j to be referenced outside the for loop. If we remove the console.log statement, then it will run.


Treating Array-Like Iterable Objects Like Arrays

In JavaScript, we can have properties with keys that are numbers. They’re automatically converted to strings before being accessed since they’re actually strings with all numerical content. Objects like the arguments and NodeList objects aren’t arrays, but they have properties stored with integer keys like they’re arrays. It’s easy to mistake them for arrays. The difference between arrays and array-like objects is that array-like objects aren’t arrays, but they both have a function with the Symbol Symbol.Iterator as the identifier. In these cases, we can convert them to arrays. For example, if we want to get the arguments passed into a regular function with the arguments object, we write something like the following with the spread operator:

function f() {  
  const args = [...arguments];  
  console.log(args);  
}  
f(1, 2, 3);

If we run the code above, we get [1,2,3].


Confusing Non-Array Objects With Arrays

Since we can access object properties and array entries with the bracket notation, which looks the same when used for arrays and regular objects, it’s easy to confuse objects and arrays.

For example, suppose we have the following code:

const obj = {  
  0: 1,  
  1: 2,  
  2: 3  
};  
console.log(obj[0]);

We see that the console.log statement would return 1. The code above has a regular object, but in the console.log statement, we pass in 0 as the key with the bracket notation to get the value obj[0], which is 1.

If we have an array and we try to access an entry by its index like in the code below:

const arr = [1, 2, 3];  
console.log(arr[0]);

we also get 1 with the console.log statement. They both use the bracket notation to access their values, but they aren’t the same. Arrays are objects, but you can iterate through them, unlike regular objects. If you try to loop through an array with the for...of loop or the forEach function, or try to use the spread operator with it, the example with the obj object will result in an error since it’s not an iterable object. We can make it iterable by adding a generator function with the Symbol Symbol.iterator to it like in the following code:

const obj = {  
  0: 1,  
  1: 2,  
  2: 3,  
  [Symbol.iterator]: function*() {  
    for (let prop in this) {  
      yield this[prop];  
    }  
  }  
};

Then when we iterate the obj object with the for...of loop like the code below:

for (let num of obj) {  
  console.log(num);  
}

we get back the entries of the new obj object that we made iterable.

The spread operator would also work. If we have the following code:

console.log([...obj]);

we get [1, 2, 3] from the console.log output.


Most of these tricks to avoid mistakes include features from ES6. It’s a major reason why ES6 was released. Generators and the let and const keywords are all part of the specification. The use of ES6, along with better text editors like Visual Studio Code, help us avoid mistakes that we would otherwise would make with older technologies. It’s a great reason to keep JavaScript code and tooling up to date. ES6 was released in 2015, so there’s no reason to stay back. For older browsers like Internet Explorer, we can use something like Babel to convert it to ES5 on the fly. Or we can use a module bundler like Webpack to convert ES6 or later code to ES5 in the build artifact so that we can write code with newer versions of JavaScript.