Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Variables and Objects

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at what JavaScript features should be used to write our JavaScript apps.

Writing Blocked-Scoped Code

Keeping all our code in blocks and variables inside it within the block’s scope is important because it reduces lots of confusion when people are reading code.

If everything is block-scoped, then we know that everything inside a block can only be accessed within the block. Therefore, it’s impossible for anyone to assign values to a block-scoped variable outside the block.

This means that we should never use var , we should always use strict mode, and we should always use let and const to declare variables and constants respectively.

let and const are both block-scoped, so that they’re only available in the block that they’re declared in.

For instance, we can create a code block with let and const variables and constants as follows:

if (foo){
 let x = 1;
}

In the code above, we have an if block with the variable declaration for x inside it.

When we try to reference it anywhere outside the if block, we’ll get an error since it’s not accessible outside the block. This rule never changes.

Unlike variables declared with var , there’s no hoisting, and it can’t be accessed outside the block.

var variables are accessible within the entire function, which is probably not what we want as it creates confusion on where we can reference it. Therefore, we shouldn’t use it.

Since ES2015, we can also define standalone code blocks in addition to if , switch , and functions.

This also limits the access of our variables and constants to that block if we use let and const to declare the variables and constants respectively.

For instance, we can do that as follows:

{
  let x = 1;
  const y = 2;
}

In the code above, we defined a block and then declared the x variable and y constant.

They can only be referenced inside the curly braces. This is much better than declaring a function and running it immediately since it’s less typing and we don’t have to declare another function unnecessarily.

const is different from let in that we can’t reassign something that’s declared with const . However, we can still modify the assigned object if we wish.

The Spread Operator

The spread operator, which is denoted by 3 periods, spreads array entries and object values to another array or object respectively.

This lets us create objects and arrays by merging one or more of them together.

We can also spread arrays into objects, which creates an object with the indexes of the original array as the keys and the corresponding entries as the values.

This is a quick and easy to understand method to merge multiple objects or arrays or make copies of them without writing any loops or anything more complex.

For instance, we can spread and object as follows:

const arr = [1, 2]
const obj = {
  ...arr
};

Then we get:

{
  "0": 1,
  "1": 2
}

as the value of obj as we expected.

Also, we can make a copy of an object as follows:

const obj = {
  a: 1
};
const copy = {
  ...obj
};

In the code above, we used the spread operator to make a shallow copy of obj by using the spread operator to spread the entries into the copy object.

Then we should see the same keys and values in both constants, but when we log obj === copy , we’ll see that the expression returns false since the spread operator creates a copy instead of referencing the original object.

The spread operator is also very handy for merging objects and arrays into one.

For objects, we can write the following to combine multiple object’s own properties into one object as follows:

const obj1 = {
  a: 1
};
const obj2 = {
  b: 2,
  a: 3
};
const merged = {
  ...obj1,
  ...obj2
}

In the code above, we merged obj1 and obj2 into a new object called merged using the spread operator. If 2 keys are the same, then the one that’s merged in later is kept.

Therefore, we see that merged is:

{
  "a": 3,
  "b": 2
}

We can also use that with arrays. All entries of the original array are kept. For instance, we can write:

const arr1 = [1, 2]
const arr2 = [3, 4, 5]
const merged = [...arr1, ...arr2]

Then merged is:

[
  1,
  2,
  3,
  4,
  5
]

The spread operator is a short, less error-prone, and easy to understand method to copy and merge objects and arrays, so it should be used for such purpose instead of writing anything more complex.

Conclusion

Some of the best JavaScript features that are released in the last few years are block-scoped variables and constants and the spread operator.

Block-scoped variables and constants reduces the chance of error by creating variables and constants that are always block-scoped, so it’s harder for values to be changed unintentionally.

The spread operator lets us copy and merge objects and arrays in a more reliable way.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Good and Bad Features

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at some good features of JavaScript that we should use to write robust JavaScript code.

Use === Instead of ==

In JavaScript, there’s the === and == operators for determining the equality of 2 entities.

== coerces the type by following a long list of rules before doing the comparison between its 2 operands.

On the other hand, === doesn’t do any data type coercion before comparing its operands.

This makes === better since JavaScript’s data type coercion creates weird results that we don’t expect.

Using the == operator leads to expressions like 0 == false returning true .

We don’t want to make things a lot harder by saving one character on our code, so we should always use === to do equality comparisons.

Likewise, to check for inequality, we should use the !== operator, which also doesn’t do any data type coercion before comparing its operands for inequality.

Never Use Eval

eval lets us run code that’s embedded in a string. This means that theoretically, any user can run anything as long as they can set the value of the string that we pass into eval .

The code in eval runs in global scope so it can have a big impact if we let anything run with eval .

Therefore, we should avoid this at all costs. Also, it decreases our app’s performance since running code from a string means that the JavaScript interpreter can’t do any performance optimizations beforehand.

Never Use the Function Constructor

The Function constructor is like eval . It lets us define a function by passing in the code that we want to run inside the function.

For instance, we can pass in the parameter names and the function body as strings by writing the following:

const add = new Function('x', 'y', 'return x + y');

In the code above, we invoked the Function constructor with the 'x' and 'y' strings, which are the parameter names. Then we have 'return x + y' as the 3rd argument to return the sum of x and y .

We can then run our function as follows:

const sum = add(1, 2);

Like eval , we dynamically pass in strings to write our code. It also runs everything in the global scope and prevents optimizations just like eval does.

It also has the same security issues as eval since we still pass in strings to define our functions.

Therefore, we also shouldn’t use this to define our functions.

Don’t Pass in Strings as Code in setTimeout and setInterval

The setTimeout and setInterval functions allow us to run code after a delay and run code in a specified interval respectively.

One feature we shouldn’t use in either of them is passing strings into them to run code.

For instance, we shouldn’t write something like the following:

setTimeout('console.log("foo")', 100)

This will run console.log("foo") after 100 milliseconds, but we shouldn’t do this since again, we’re running code from a string which presents the same security and performance issues as the other occasions where we run code from a string.

Therefore, we should always run code that’s defined with code instead of a string, like the following:

setTimeout(() => console.log("foo"), 100)

The code above runs console.log like it does with the string, but the JavaScript interpreter can optimize the code and there’re no security issues since there’s no risk that attackers can inject code into our app as a string.

Always Add Semicolons and Parentheses

Semicolons should always be added to make our code as clear as possible. JavaScript can insert semicolons automatically when we don’t do it ourselves. Parentheses can also be optional with one-line if blocks.

It’ll insert it when it thinks it should be a new line, like in any logical break that it finds.

However, it often leads to confusion. If we write something like the following:

if (foo)  
  x = false  
  bar();

Then we may think that both lines are run if foo is true . But this isn’t the case.

The JavaScript interpreter will think that only x = false is part of the if block and bar() is separate.

Therefore, if we want to eliminate confusing code like this, we should write:

if (foo) {  
  x = false;  
  bar();  
}

to delimit our lines and blocks with semicolons and parentheses.

This way, we know that they both will run if foo is true .

Conclusion

=== and !== are always better than == and != for equality and inequality comparisons respectively.

Also, we should never run code from a string with eval , the Function constructor, setTimeout , setInterval or any other place that allows us to do that.

Finally, we should add semicolons and parentheses to delimit our code blocks.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Tools and Tests

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at some tooling and testing best practices for writing robust JavaScript code.

Use TypeScript

TypeScript is a language that’s an extension of JavaScript to provide flexible and optional type checking for JavaScript code.

It builds code into JavaScript so that it can be run on the browser or in the Node.js environment.

The code is checked for type errors before the code is built into the final artifact. This means that data type errors, which would be runtime errors if it weren’t checked by TypeScript are prevented.

This is a big class of errors that are eliminated. The type checking isn’t rigid. It provides many ways to annotate the types of objects and return types of functions.

For instance, we can create interfaces to annotate the types of objects as follows:

interface Pet {
    name: string;
    walk(): void;
}

In the code above, we created an interface to indicate that whatever has the type Pet must have the name string property and a walk method that returns nothing (hence the void return type).

Then we can use that as follows:

const dog: Pet = {
    name: 'joe',
    walk() {
        console.log('walk');
    }
}

In the code above, we have the name string property and the walk method in the dog object, which meets the requirement of the interface.

Therefore, the code will build and run by the TypeScript compiler. We’ll get an error if the types of anything are different from the interface.

Another good feature of TypeScript is the flexibility of the type checking. The types don’t have to be fixed, they can be flexible and dynamic as well.

For instance, we can create nullable types as follows:

interface Pet {
    name: string;
    walk?(): void;
}

In the code above, the walk method has been made optional with the ? after the method name. Now we can omit it in any object that has type Pet .

We can also create union types of multiple types as follows:

interface Pet {
    name: string;
    walk(): void;
}

interface Animal {
    breed: string;
}

const dog: Pet | Animal = {
    name: 'joe',
    breed: 'labrador',
    walk(){}
}

In the code above, we have the Pet | Animal type, which consists of the members of the Pet and Animal types combined together.

Therefore, our dog object can have any member from each interface included in it.

Also, we don’t have to specify the types of an object directly. If we already have an object in our code and we want another object to have the same data type as that one, we can use the typeof operator as follows to specify the type:

const cat = {
    name: 'james'
}

const dog: typeof cat = {
    name: 'joe'
}

In the code above, we specified the data type of the dog object to be the same as the data type for cat with the typeof dog expression. This way, we don’t have to specify any types directly.

TypeScript has many more features that can help us write more solid JavaScript code by preventing data type errors that are otherwise caught at runtime.

This is especially important with big apps where there’s more chance of these errors happening.

Therefore, we should consider using TypeScript for writing more robust JavaScript code.

Photo by Jerry Wang on Unsplash

Write Tests

We should write automated tests to check for regressions in our code so that we can have peace of mind when changing code and all the automated tests pass.

It’s easy to write automated tests with the Jest test runner, which has many features to let us write and run tests for both front and back end JavaScript apps.

For instance, we can write a simple function with unit test as follows:

math.js :

const multiply = (a, b) => {
  return a * b;
}
module.exports = {
  multiply
};

math.test.js :

const math = require('./math');

test('multiplies 1 * 2 to equal 2', () => {
  expect(math.multiply(1, 2)).toBe(2);
});

In the code above, the math.js module has the function multiply that we want to test.

Then in math.test.js , we added our test for testing the multiply function exported by math.js .

The file names are the convention for Jest tests, where we have the name of the production code file corresponds with the name of the test file.

Then when we run Jest, this test will run and should pass because 1 times 2 is 2.

If we write unit tests for every part of our app then it’s easy to know whether our app is still working properly.

Conclusion

TypeScript is probably the best type checker for JavaScript. It’s a natural extension of JavaScript to add type annotations and checking to our JavaScript code.

Writing automated tests is always a good idea so that we can check that our code is working properly after we make changes to it.

Categories
JavaScript Best Practices

JavaScript Best Practices — Code That We Shouldn’t Have

JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.

In this article, we look at some code that we shouldn’t add to our JavaScript codebase.

No Unnecessary Labels

JavaScript has labels that we can label parts of our code. For instance, if we have 2 loops, we can label them as follows:

A: while (a) {
  break A;
}

B: while (b) {
  break B;
}

In the code above, we have label A to label the first while loop and B to label the second while loop.

However, the example above is useless because the label is just referenced within itself. So the code above is the same as:

while (a) {
  break;
}

while (b) {
  break;
}

Therefore, the labels are useless.

We should use labels to only reference other pieces of code which the label is used to label. For instance, we can write the following code:

A: while (a) {
  break B;
}

B: while (b) {
  break A;
}

No Case Statement Fallthrough

In switch blocks, we can write case statements or blocks without the break keyword at the end of each case .

This isn’t good because all the case statements or blocks will run even if a matching case is found within the switch statement.

For instance, if we have:

let foo = 1;
let bar;
switch (foo) {
  case 1:
    bar = 'x';
  case 2:
    bar = 'y';
}

Then we’ll see that bar is 'y' even though foo is 1, which is probably not what we want.

The correct way to write the switch statement is to write the following:

let foo = 1;
let bar;
switch (foo) {
  case 1:
    bar = 'x';
    break;
  case 2:
    bar = 'y';
    break;
}

Then bar will be 'x' as we expected. Alternatively, we can also write return instead of break as follows:

let foo = 1;
const baz = (foo) => {
  switch (foo) {
    case 1:
      return 'x';
    case 2:
      return 'y';
  }
}

let bar = baz(foo);

In the code above, we have return statements instead of break, and we placed the switch statement in the function so we can get the return value when calling it.

It’ll give us the same result as using break since it ends the code execution of the function.

No Floating Decimals

Floating decimals can be before or after a number in JavaScript. For instance, we can write the following:

let num1 = .1;
let num2 = 3.;
let num3 = -.5;

In the code above, we defined numbers with decimal points before or after a number.

This isn’t good because it’s not clear that the dot is for the dot operator for accessing properties or whether it’s a decimal point.

Instead, we should just put in the leading and trailing zeroes as follows:

let num1 = 0.1;
let num2 = 3.0;
let num3 = -0.5;

Photo by Vladislav Klapin on Unsplash

Assignment to Native Objects or Read-Only Global Variables

Read-only global variables like window or Object are read-only. Therefore, we shouldn’t be assigning new values to it.

For instance, we don’t want to do things like:

window = {};

since we don’t want window to be an empty object. Other things we don’t want include code like:

Object = null;
undefined = 1;
null = 2;

JavaScript strict mode should prevent these assignments from happening. If we didn’t use strict mode, then we should make sure that we don’t do things like this.

Modules are automatically set to use strict mode, so we don’t have to worry about this.

No Type Conversion with Shorter Notations

We can use the unary + , - , or ~ to convert values to numbers or boolean.

They aren’t very clear to some people, so we may want to limit their use. Therefore, instead of writing:

let b = !!foo;
let c = +'2';

We should convert them explicitly with built-in JavaScript functions as follows:

let b = Boolean(foo);
let c = Number('2');

Conclusion

We shouldn’t write code that is useless or don’t do what we expect them to. For instance, case statements and blocks in switch statements should always have break or return added.

Decimals should either not be used or we add leading and trailing zeroes to make it clear.

Also, we should never assign native global objects with our own values.

Finally, we may want to convert data types explicitly with our own code to make our type conversion code more clear.

Categories
JavaScript Best Practices

JavaScript Best Practices — More Code That Shouldn’t be Added

JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.

In this article, we’ll look at more code that shouldn’t be added to our JavaScript code.

No Implicit Global Variable Declaration

A global variable declaration can be done in various ways in JavaScript. Without strict mode enabled, we can declare a global variable with var .

For instance, we can write something like the following to create global variables without strict mode:

var x = 1;

Since the code above is in the top level, if we have strict mode off, we’ll declare a variable in the global scope.

In the browser, the code above is the same as window.x = 1 .

Therefore, using var isn’t the best way to declare a variable at the top level without strict mode.

Now that we have let and const , we should use those instead since they won’t declare a global scope, and also, their scope is consistent anywhere. They always declare variables in a block scope.

Even in the top-level, they won’t declare global variables. Preventing declaring global variables accidentally can also be done with enabling strict mode.

This way, the example we have above won’t attach x as a property of window .

No Implied eval() Code

setTimeout and setInterval can accept a string that runs the code as a their callback.

For instance, the following:

setTimeout("console.log('foo');", 100);

does the same thing as:

setTimeout(() => console.log('foo'), 100);

setInterval has the same feature, so:

setInterval("console.log('foo');", 1000);

does the same thing as:

setInterval(() => console.log('foo'), 1000);

This means that we can insert any string into either function and run the code.

The string is interpreted in the global scope, so anybody can set variables inside themselves if they wish and run arbitrary code. Running code from a string is also slower. Therefore, we should avoid passing strings into setInterval and setTimeout .

Instead, we should pass in a callback.

No this Keywords Outside of Classes or Class-Like Objects

The keyword this shouldn’t be used where it isn’t needed. Therefore, it shouldn’t be used outside of constructor functions, object literals or classes.

Object literals can have this inside its methods. Getters and setters inside objects, constructor functions, and classes can also reference this since they’re part of an instance.

this can also be referenced in callbacks and traditional functions that calls call , apply , or bind .

For instance, we can write the following code:

function() {
  this.foo = 0;
}

The following is also good:

class Foo {
  constructor(foo) {
    this.foo = foo;
  }
}

Things like arrow functions should have this in it, since it doesn’t bind to this , so:

() => this.foo;

isn’t proper JavaScript code.

We can also use call as follows:

(function foo() {
  console.log(this.foo)
}).call({
  foo: 'bar'
})

Then we see that the console log outputs 'bar' , which is the valur of foo that we pass into call .

Photo by Kevin Mueller on Unsplash

Disallow Iterator

The __iterator__ property was a custom property of an object’s prototype that’s used to create a custom iterator for for...in loops.

Now that JavaScript has iterators, we don’t need this. For instance, the following is now obsolete code:

Bar.prototype.__iterator__ = function() {
    return new BarIterator(this);
}

We shouldn’t write code like that anymore now.

Disallow Labeled Statements

We can add labels to JavaScript in conjunction with break and continue to control the flow of multiple loops.

For instance, if we have:

outer:
  for (let i = 0; i < 5; i++) {
    while (true) {
      break outer;
    }
  }

Then we end the for loop when the while loop is first executed. The better way to use break and continue is without labels.

The code above is more error-prone and harder to understand than the code without labels.

For instance, we should write:

while (true) {
  break;
}

so we don’t have labels.

Conclusion

A global variable declaration shouldn’t be added accidentally to our JavaScript code. To do this, we should make sure strict mode is on and we aren’t using var if it’s off.

We can pass in a string to setTimeout and setInterval to run code. They’re all run in the global scope. This lets anyone sets global variables and run code as they wish. Also, it’s slower than running the actual code. Therefore, we shouldn’t use strings as callbacks in those functions.

this should only be included when it’s needed. They’re needed only in constructor functions, classes, and object methods.

The __iterator__ is obsolete and shouldn’t be used. Finally, labels are also not commonly used for marking loops for reference and shouldn’t bee used since it’s error-prone and hard to read.