Categories
JavaScript Best Practices

JavaScript Best Practices — Comments and Spaces

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 the best ways to work with comments and spaces.

Comments

We should be careful when writing JavaScript. We want them to be readable like anything else.

Use /** ... */ for Multiline Comments

The /** ... */ block denotes that a multiline comment inside it.

It’s better than // since we can write our comments in one block.

For example, instead of writing:

// foo
// bar

We can write:

/**
 * foo
 * bar
 */

Use // for Single Line Comments

// is great for single-line comments. We should put a single line above the subject of the comment.

And an empty line should be added before the comment.

For instance, we should write:

function getType() {
  // returns type
}

or:

function getType() {
  console.log('get type');

// returns type
}

Prefix FIXME and TODO to Label Code that Needs Changing Later

We can change FIXME and TODO to label code that needs changing later.

This way, we won’t forget about them.

We just have to remember to remove comments after we’re done.

For instance, we can write:

// TODO - clean up code

If our code has Problems, then use // FIXME to Mark Them

For instance, we can write:

// FIXME - remove global variable

so that we can fix that.

Use // TODO to Mark Solutions to a Problem

If we have a solution that we don’t have time to implement yet but we have an idea of how to do it, we should use // TODO to mark that.

For instance, we can write:

// TODO - make total configurable

to mark the solution to some problem that hasn’t been implemented yet.

Whitespace

Spaces make things easier to read, so we should add them consistently.

Use Soft Tabs Set to 2 Spaces

Spaces are consistent among operating systems, so they’re better than tabs.

2 spaces make things readable without too much typing.

So instead of writing:

function foo (){
 let x;
}

We write:

function foo (){
  let x;
}

We can also configure our text editor to convert 1 tab to 2 spaces automatically to save typing.

Place 1 Space Before Leading Brace

We should place 1 space before a leading brace so that we can read our functions easier.

For instance, instead of writing:

function foo(){
  console.log('foo');
}

We write:

function foo() {
  console.log('foo');
}

Likewise, with arrow functions, instead of writing:

const foo = () =>{
  console.log('foo');
}

We write:

const foo = () => {
  console.log('foo');
}

With function calls, instead of writing:

person.set('attrs',{
  age: 1,
  gender: 'male'
});

We write:

person.set('attrs', {
  age: 1,
  gender: 'male'
});

Just one space makes things more readable.

Place 1 Space Before the Opening Parenthesis in Control Statements

If we’re writing conditional statements or loops, we should have 1 space before the opening parenthesis.

And we should have no pace between the argument list and the function names in function calls and declarations.

For instance, instead of writing:

if(foo) {
  bar ();
}

We should write:

if (foo) {
  bar();
}

Likewise, for loops, instead of writing:

while(foo) {
  bar();
}

We write:

while (foo) {
  bar();
}

For function definitions, instead of writing:

function foo () {
  console.log ('bar');
}

We write:

function foo() {
  console.log('bar');
}

The spacing makes our blocks more readable.

Set Off Operators with Spaces

We should have spaces between operands and operators to improve readability.

For instance, instead of writing:

const x=y+1;

We write:

const x = y + 1;

End File with a Single New Line Character

We should always end a file with a single newline character.

This way, they can always be concatenated properly by programs that do that if needed.

For instance, instead of writing:

index.js

const x = 1;

We write:

index.js

const x = 1;


Conclusion

Spacing is important. Therefore, we should have them in places where they make sense.

If we write comments, we should denote multiline comments in a different way than single-line comments.

We may also use comments to denote todo items and things that need fixing.

Newline character should be at the end of file so that it can be concatenated properly with other files.

Categories
JavaScript Best Practices

JavaScript Best Practices —Names, jQuery, 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 the best ways to name things and deal with objects, names, and jQuery code.

Use PascalCase for Constructor, Class, Singleton or Library Exports

We stick with PascalCase as we do without export for constructors, classes, singleton objects, and libraries.

For instance, we write:

const StudentUser = {
  //...
};

export default StudentUser;

This also applies to constructors and constructor functions:

class StudentUser {
  //...
}

function StudentUser {
  //...
}

Acronyms Should be All Upper Case or Lower Case

Having acronyms all in the same case makes them more readable.

For instance, we can write:

const HTTPResponses = [
  // ...
];

or:

const httpResponses = [
  // ...
];

When Should we Make Constant Names Upper Case?

We can make constant name supper case when the constant never changes.

Having them upper case means that we can trust to not change.

We should also make constant names upper case if they’re exported.

However, that should only apply to the top level.

For instance, we can write:

export const API_KEY = 'foo';

or:

export const FOO = {
  key: 'value'
};

Accessors

We don’t need accessor functions for properties.

Also, we shouldn’t use JavaScript getters or setters as they have unexpected side effects.

They also make our code harder to understand.

Instead, we make functions to get and set our values.

For instance, instead of writing:

class Person {
  get age() {
    // ...
  }

  set age(value) {
    // ...
  }
}

We write:

class Person {
  getAge() {
    // ...
  }

  setAge(value) {
    // ...
  }
}

Normal methods are easier to understand.

Is a Property or Method returns a Boolean, then we Prefix it with is or has

For instance, we write:

person.hasAge()

instead of:

person.age()

Now we know what the function does just by the name.

Creating get and set Functions

We can create get and set functions for getting and setting values.

For instance, we can write:

class Person {
  constructor() {
    //...
  }

  set(key, val) {
    this[key] = val;
  }

  get(key) {
    return this[key];
  }
}

This way, we can get and set the values dynamically by their key and value.

Triggering Events

If we trigger events, we should pass in an object instead of a raw value.

For instance, we write:

const event = new CustomEvent("eventName", {
  "detail": "event"
});
document.dispatchEvent(event);

document.addEventListener("eventName", (e) => {
  console.log(e.detail);
});

Now we have an object as the value of detail instead of a primitive value.

If we always send an object with an event, then we won’t have to check if the object sent is a primitive value or an object.

jQuery

If we use jQuery , we should add a $ prefix so that we know it’s something returned by jQuery.

For instance, we can write:

const $main = $('.main');

Cache jQuery Lookups

If we look up something with jQuery, we should cache them so that we don’t have to look up the same thing repeatedly.

For instance, we can write:

const main = $('.main');
main.hide();
// ...

main.css({
  'background-color': 'green',
});

Use find with scoped jQuery Object Queries

We shouldn’t use find to make scoped queries.

Instead, we just use one CSS selector with one query:

$('.main ul').hide();

This way, we only do the lookup once rather than twice.

Arrow Functions

We should use arrow functions as much as we can for callbacks.

For instance, we write:

[1, 2, 3].map((x) => x + 1);

It’s much shorter than using traditional functions.

The value of this also doesn’t change inside it.

Skipping Braces

We can skip the braces and use implicit return if our function is one statement long.

The example above showed us that we can skip the braces and return something.

We wrote:

(x) => x + 1

which returns x plus 1.

Conclusion

To make things speedier when using jQuery, we should cache our queries.

Also, we shouldn’t use find to make scoped queries. Instead, use one CSS selector.

We should stick to standard JavaScript naming conventions.

And we should use the get and set keyword for accessors.

Categories
JavaScript Best Practices

JavaScript Best Practices — Documentation

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 the best practices for writing documentation with JSDoc.

Top/File-Level Comments

A file may name things like a copyright notice, author information in the comments.

We may also include the description of the file contents if it doesn’t repeat what’s in the code.

Class Comments

A class may have its own comments with the description and the parameters.

For instance, we can write:

/**
 * A class to store Fruit data
 */
class Fruit extends Food {
  /**
   * @param {string} name An argument for the name.
   * @param {Array<number>} dimensions List of numbers for the
   *    dimensions.
   */
  constructor(name, dimensions) {
    // ...
  }
};

We have a description of the class above the class.

And inside the class, we have a comment block to explain the items in the constructor.

Each parameter has its own explanation of what it holds and the type of them.

This way, we can use programs to help us create documentation if needed.

Enum Comments

We can write comments for an enum.

For instance, we can write:

/**
 * Types of weight units.
 * @enum {string}
 */
const WeightType = {
  GRAM: 'gram',
  POUND: 'type',
};

We added a comment for an enum that has a list of weight units.

The comment has an @enum tag to explain what each enum value’s type is.

In this example, they’re all strings.

Method and Function Comments

Like with classes and objects, we can add comments to methods.

For instance, we can write:

/** A class */
class FooClass extends BaseClass {
  /**
   * Operates on a FooClass instance and returns item.
   * @param {!FooClass} foo Some object.
   * @param {!OtherClass} other Another object.
   * @return {boolean} Whether something occurred.
   */
  doSomething(foo, other) { ... }

  /** @override */
  overriddenMethod(param) { ... }
}

In the code above, we have a comment for the clas at the top.

Then we have a comment block to describe what a method does.

In addition, we have an explosion of what each parameter can hold and the data type of them.

Also, we have the @override tag to explain that the method overrides a method of the parent class.

Likewise, we can have something similar in function.

For example, we can write:

/**
 * A function that makes an object.
 * @param {TYPE} arg
 * @return {!TYPE2}
 * @template TYPE
 */
function makeObject(arg) { ... }

We have a function that takes a given type of argument, so we write a comment to explain that.

Also, we have the @return tag to return some object of TYPE2 .

Property Comments

Also, we can add comments to properties.

For instance, we can write:

/** A class. */
class AClass {
  /** @param {string=} arg */
  constructor(arg = 'something') {
    /** @const {string} */
    this.arg = arg;
  }
}

We have a class that has a constructor with some comments.

We added a comment for the constructor’s argument.

Also, we have another one for the instance variable itself.

We indicated that it’s constant with the @const tag.

Type Annotations

We add data type annotations after @param , @return , @this , and @type tags.

Also, we can add them optionally on @const , @export and any visibility tags.

Nullability

The modifiers ! and ? denote non-nullable and nullable respectively.

Also, we add type annotations for primitives, including string , numbner , boolean , symbol , undefined , and null .

Literal types of annotations include functions:

{function(...): ...}

and objects:

{{foo: string...}}

Reference types are UpperCamelCase.

Template Parameter Types

We should specific parameters in types.

For instance, we write:

const /** !Array<string> */ names = [];

if our array is a string array.

Conclusion

We can add comments for parameters, constructors, classes, and object properties.

Information like the type of them, if applicable, what a function returns, etc. can be added.

On top of a file, we can put information like a copyright notice or something like that.

There are also symbols for indicating nullability.

Categories
JavaScript Best Practices

JavaScript Best Practices — Comparisons

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 the best ways to work with switch statements and other comparisons statements.

Use Braces to Create Blocks in switch and case Statements

If we create blocks in switch and case statements, then we can enclose block-scoped variables.

So instead of writing:

switch (foo) {  
  case 1:  
    let x = 1;  
    break;  
  case 2:  
    const y = 2;  
    break;  
    //...  
}

We can write:

switch (foo) {  
  case 1: {  
    let x = 1;  
    break;  
  }  
  case 2: {  
    let x = 2;  
    break;  
  }  
  //...  
}

We can use x twice since x isn’t available outside the case block.

Terneries Shouldn’t be Nested

Ternary expressions shouldn’t be nested since they’re hard to read.

They should also be short.

For instance, instead of writing:

const foo = val1 === val2 ?  
  "foo" :  
  val3 > val4 ? "baz" : null;

As we can see, that’s hard to understand.

Therefore, we should write the following instead:

const foo = val1 === val2 ?  
  "foo" : "baz";

Avoid Useless Ternary Expressions

We shouldn’t have useless ternary expressions.

For instance, the following isn’t needed:

const baz = c ? false : true;

If c is a boolean, we can just assign it directly to baz .

If it’s not, we can use the Boolean function to convert it to a boolean.

So we can write:

const baz = !Boolean(c);

instead.

Enclosed Expressions with Mixed Operators in Parentheses

We should enclose expressions with mixed operators in parentheses to make them easier to read.

For instance, we shouldn’t write:

const foo = a && b < 5 || c < 3 || d + 1 === 0;

Instead, we should write:

const foo = ((a && b < 5) || (c < 3) || (d + 1)) === 0;

Now we can see the expressions clearly since they’re separated by parentheses.

Blocks

There are some things to consider when we’re creating blocks.

Use Braces with Multiline Blocks

If we have blocks, then we should dd braces whether they’re required or not.

For instance, instead of writing:

if (test)  
  return true;

We write:

if (test) {  
  return true;  
}

Likewise, with functions, instead of writing:

function foo() { return true; }

We write:

function foo() {   
  return true;   
}

If we’re Using Multiline Blocks with if and else, put else on the Same Line with if Block’s Closing Brace

For instance, instead of writing:

if (test) {  
  foo();  
  bar();  
}  
else {  
  baz();  
}

We write:

if (test) {  
  foo();  
  bar();  
} else {  
  baz();  
}

It saves some space without impacting readability.

If an if Block Always Runs a return Statement, the Subsequent else Block isn’t Necessary

If we have an if block that always returns, then the else block will never be run.

Therefore, we should remove the else block and put the return outside it.

For instance, instead of writing:

const foo = () => {  
  if (x) {  
    return x;  
  } else {  
    return y;  
  }  
}

We write:

const foo = () => {  
  if (x) {  
    return x;  
  }  
  return y;  
}

Control Statements

There are better ways to deal with control statements than others in JavaScript.

If Our Control Statements are too Long, then Each Condition Should be in a New Line

For instance, if we have:

if ((foo === 123 || bar === 'abc') && suprSuperSuperLong() && isSuprSuperSuperLong()) {  
  doSomething();  
}

Then we should tidy it up by writing:

if (  
  (foo === 123 || bar === 'abc') &&  
  suprSuperSuperLong() &&  
  isSuprSuperSuperLong()  
) {  
  doSomething();  
}

This is much easier to read than the first example.

No Selection Operator in Place of Control Statements

Using if is easier to understand than using the && operator to run something if a condition is true .

For instance, instead of writing:

canRun && run();

We write:

if (canRun) {  
  run();  
}

Conclusion

We should surround swiotchj and case statements with braces so that we can use block-scoped variables with them.

If we long boolean expressions, then we should break them into their own line.

Categories
JavaScript Best Practices

JavaScript Best Practices — Variables and Comparisons

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 the best ways to work with variables and comparisons.

No Unused Variables

Unused variables are useless, so we shouldn’t include them.

For instance, if we have:

let x = 1;

but x isn’t used anywhere, then we should remove x .

Hoisting

var variables are hoisted and that tricks people and causes problems.

var Declarations are Hoisted to the Top, but their Value Isn’t

var declarations are hoisted but their value isn’t.

For instance, if we have:

console.log(x);
var x = 1;

then x is undefined .

But if we have:

var x = 1;
console.log(x);

Then x is 1.

This is a common source of confusion.

Therefore, it’s one reason that we shouldn’t use var to declare variables.

Use let or const instead to declare them.

Anonymous Function Expressions Hoist their Variable Name but Not Function Assignment

Like var variables, functions that are assigned to a var variable have the variable hoisted, but the function can only be used after it’s been assigned to the variable.

For instance, if we have:

foo();
var foo = () => {}

Then we get ‘Uncaught TypeError: foo is not a function’ since foo is undefined .

On the other hand, if we write:

var foo = () => {}
foo();

We can call foo .

Named Function Expressions Hoist the Variable but Bot the Function Name or Function Body

Likewise, if we have named functions the function itself isn’t hoisted, but the variable name is.

For instance, if we have:

foo();
var foo = function bar() {
  //...
}

Then we get the ‘Uncaught TypeError: foo is not a function’ error since foo is undefined .

But we can call foo by writing:

var foo = function bar() {
  //...
}
foo();

Function Declarations Hoist Their Name and Function Body

A function declaration is a named traditional function that isn’t assigned to a variable.

For instance, the following is a function declaration:

function foo() {
  //...
}

They can be called anywhere in the script, so:

foo();

function foo() {
  //...
}

and:

function foo() {
  //...
}

foo();

both call foo .

Comparison Operators & Equality

We should be careful when we use JavaScript operators, especially with the data type conversion that it does.

Use === and !== Over == and !=

We should use === or !== for equality or inequality comparison respectively since they don’t do any data type coercion before doing the comparisons.

For instance, instead of writing:

1 == '1'

We write:

1 === '1'

Likewise, instead of writing:

1 != '1'

We write:

1 !== '1'

This way, both operands have to have the same type and content for them to be considered equal.

Conditional Statements Like if Statements Evaluate their Expressions Using Coercion

Anything expression that we put between the parentheses is coerced into a boolean before evaluation.

Objects evaluate to true .

undefined and null evaluate to false .

Booleans evaluate to the value of the boolean.

Numbers evaluate to false if it’s +0, -0, NaN . Otherwise, it’s evaluated to true .

String evaluate to false if it’s an empty string and true otherwise.

We should keep these rules in mind when we have expressions in if .

For instance, if we have:

if ({ foo: 1 }){
  //...
}

Then { foo: 1 } will be coerced to true .

Use Shortcuts to Booleans but Explicit Comparison for Strings and Numbers

We don’t need to write things like:

if (valid === true){
  //...
}

Instead, we write:

if (valid){
  //...
}

But for strings or numbers, we do need to compare explicitly.

For example, we write:

if (value === 1){
  //...
}

Conclusion

If we have unused variables, we should remove them.

If we’re doing comparisons, we should use === or !== as much as possible.

When we compare with booleans, we don’t need to explicitly compare values with booleans.

Finally, we should be careful of function expressions and declarations which may trick us.