Categories
JavaScript Best Practices

Maintainable JavaScript — Declarations and Function Calls

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at variables and functions.

Variable Declarations Hoisting

The var statements are hoisted.

This means the declaration can be accessed before it’s defined.

This is because the declaration is pulled up to the top without the value.

We don’t want that to happen automatically since it’ll confuse us.

Fortunately, there’s the let and const variables which are block-scoped and aren’t hoisted.

This is great since we won’t be tricked by where a variable is available in the code.

To make our lives even easier, we should put the variables as the first statements in a function.

A function shouldn’t be too long so we won’t have to jump too far.

For instance, we can write:

function doWork(items) {
  let i;
  const len = items.length;
  let value = 10;
  let result = value + 10;
  for (i = 0; i < len; i++) {
    console.log(items[i]);
  }
}

We used let and const everywhere.

The items are iterated through as with the for loop.

len is declared with const since it never changes.

Doug Crockford also recommended this style.

We also combine all variable declarations with one value per line.

Function Declarations

Function declarations are also hoisted by JavaScript engines like variable declarations.

So we can use a function declaration in our code before it’s declared.

For instance, we can write:

doSomething();

function doSomething() {
  console.log("hello world");
}

We called the doSomething before the function is declared.

This works because of the hoisting.

However, to reduce confusion, it’s better to declare our functions before they’re used.

So we should write:

function doSomething() {
  console.log("hello world");
}

doSomething();

instead.

ESLint and JSHint will warn when a function is used before it’s declared.

Function declarations should never appear inside block statements.

For instance, we shouldn’t write code like:

if (condition) {
  function doSomething() {
    console.log("foo");
  }
} else {
  function doSomething() {
    console.log('bar')
  }
}

This isn’t valid syntax but it’s still run by some browsers.

Therefore, it’s a gray area in the ES specification that should be avoided.

Function declarations should only be used outside of conditional statements.

This is also forbidden in the Google style guide.

Function Call Spacing

Function call spacing is something that isn’t too controversial.

Most people agree that function calls should have no spaces between the function name and the opening parentheses.

This is done to differentiate it from block statements.

For instance, we should write:

doWork(item);

instead of something like:

doWork (item);

This is to differentiate it from block statements like:

while (item) {
  // ...
}

Doug Crockford’s code conventions call this out.

Google’s style guide also recommends this style.

Immediate Function Invocation

JavaScript lets us declare anonymous functions without property names and assign those functions to variables and properties.

We can write:

const doSomething = function() {
  // ...
};

We assigned the anonymous function to a variable, so we can call it with the variable name.

It’s bad to call it without parentheses surrounding the function itself.

For instance, we shouldn’t write:

const value = function() {
  //...
  return {
    message: "hello"
  }
}();

It’s easy to confuse that with defining the function and assigning it to a variable without calling it.

Conclusion

var and function declarations are hoisted.

So they shouldn’t be used.

Also, we shouldn’t use immediate function invocations without parentheses.

Categories
JavaScript Best Practices

Maintainable JavaScript — CSS and JavaScript

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at loose coupling.

Keep JavaScript Out of CSS

IE8 allows us to run JavaScript expressions in our CSS code.

This is definitely bad since we don’t want tight coupling between CSS and JavaScript.

We could write something like:

.box {  
  backgroundColor: expression(document.body.backgroundColor);  
}

This is IE8 specific and it introduces tight coupling, so it should never be used.

CSS is reevaluated frequently and so the JavaScript will run frequently, so we shouldn’t put JavaScript code in the CSS.

It’s also hard to debug since the JavaScript code is in the CSS file, so there’s no way to trace them in any browsers.

Keep CSS Out of JavaScript

We can set CSS styles with JavaScript in any browser.

They work well together.

However, changing styles in JavaScript means that JavaScript code has to be run.

So if we have code like:

element.style.color = "green";

or

element.style.color = "red";  
element.style.left = "10px";  
element.style.top = "200px";  
element.style.visibility = "visible";

then we have to run one or more operations to change the style of one element.

This is also a problem because if we have style problems, we can’t just fiddle with the CSS to fix it.

We have to step through the JavaScript code to find the issue and fix the code.

This is definitely slower than just turning on and off the CSS and changing the values.

Another bad way to change the CSS is to change the cssText property of the element.

For instance, some people may write:

element.style.cssText = "color: green; left: 10px; top: 100px; visibility: hidden";

This is also bad because the styles are all in a string.

Therefore, we can’t just toggle them on and off and changing their values without changing the code and refreshing.

Keeping CSS out of JavaScript means that all the style information lives in the CSS.

So we write something like:

.box {  
  color: red;  
  left: 10px;  
  top: 100px;  
  visibility: visible;  
}

in our CSS files.

This way, we can fiddle with them in the browser dev console instead of changing the code and refreshing.

Also, the coupling is looser since the CSS is self-contained.

In our JavaScript code, we can toggle on the class by writing:

element.className += " box";

or:

element.classList.add("box");

And we can remove them by:

element.classList.remove("box");

CSS class names are the communication mechanism, between CSS and JavaScript.

JavaScript is used for adding and removing class names from elements throughout the lifecycle of the page.

The styles are applied by the classes un the CSS code.

Now we only need to change the CSS instead of changing manipulating styles directly.

Conclusion

We should keep JavaScript out of CSS and CSS out of JavaScript.

This way, we won’t have messy code tightly coupling code that are hard to debug.

Categories
JavaScript Best Practices

Maintainable JavaScript — Config Data

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at ways to externalize config data.

Good Ways of Detecting Properties

We can use the in operator to check if an object property exists in an object.

For instance, we can write:

const object = {
  count: 0,
};

if ("count" in object) {
  // ...
}

to check if the count property is added to the object object.

The expression in the if block heading will return true so the block will run.

This checks if the count property is in the object itself and whether it’s in its prototype chain.

To check if a property is a non-inherited property of an object, we can use the hasOwnProperty method.

For instance, we can write:

const object = {
  count: 0,
};

if (object.hasOwnProperty('count')) {
  // ...
}

It returns true if 'count' is an own property of object and false otherwise.

If we aren’t sure whether hasOwnProperty exists in object , we can write:

if ("hasOwnProperty" in object && object.hasOwnProperty('count')) {
  // ...
}

Now we know for sure that hasOwnProperty exists before calling it.

Separate Configuration Data from Code

Configuration data is any hardcoded values in our app.

If we have:

function validate(value) {
  if (!value) {
    console.log("Invalid value");
    location.href = "/errors/invalid";
  }
}

then we have 2 pieces of config data in our code.

One is the 'Invalid value' string.

And the 2nd is the '/error/invalid' URL string.

URLs and messages may change, so we may separate them so that we can define one reusable variable for each and then reference that everywhere else.

Data that are configuration data include:

  • URLs
  • Strings that are displayed in the UI
  • Repeated unique values
  • Settings
  • Any value that may change

We don’t want to modify multiple parts of our source code just to change some config values.

Externalizing Configuration Data

The first step to separating configuration data from our code is to externalize the config data.

This means getting the data out of the middle of our JavaScript code.

Instead of what we have before, we instead write:

const config = {
  MESSAGE_INVALID_VALUE: "Invalid value",
  URL_INVALID: "/errors/invalid.php",
};

function validate(value) {
  if (!value) {
    console.log(config.MESSAGE_INVALID_VALUE);
    location.href = config.URL_INVALID;
  }
}

We created a config object which has the config data in its own location,

Then we referenced it in our code.

Each property in config is a piece of data.

The property is uppercase so that we can tell them apart from other properties.

The most important part is externalizing the data,.

The rest is up to our own preference.

Conclusion

Config data is hardcoded data that are used in multiple places.

We should externalize our config data so that we can use them in multiple places without repetition.

This way, we can change it once and don’t have to worry.

Categories
JavaScript Best Practices

Maintainable JavaScript — Comparisons and Code Strings

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at comparisons and eval.

Boolean Comparisons

Boolean comparisons with == and != also do things we don’t expect.

This is because any operands are converted to true or false before comparison is done.

For instance, if we have:

console.log(1 == true);

then we get true because 1 is converted to true .

On the other hand, if we have:

console.log(0 == false);

we also long true since 0 is falsy and it’s converted to false .

Object Comparisons

If objects are compared then, it’s converted to a primitive value with the valueOf method.

If valueOf isn’t present, then toString is called instead.

Then the comparison continues like the other kinds of comparisons.

For if we have:

const object = {
  toString() {
    return "0xFF";
  }
};
console.log(object == 255);

Then console log logs true since object is converted to a string with the toString method.

Then the Number function converts that to a decimal number.

The null and undefined Values

Also, type coercion occurs between null and undefined is also done.

They’re deemed equivalent by the == operator.

For instance, if we have:

console.log(null == undefined);

then that logs true .

=== and !==

Type coercion is done with == and != , so we shouldn’t use them for comparisons.

The coercion rules are too complex.

Instead, we should use the === and !== operators, which don’t do any type coercion.

So if we have:

console.log(5 === "5");

we log false .

And:

console.log(25 === "0x19");

also logs false .

And also if we have:

const object = {
  toString() {
    return "0xFF";
  }
};
console.log(object == 255);

that also logs false because there’s no type coercion done.

Using === and !== is recommended by Doug Crockford’s style guide, Google style guide, etc.

This is almost a universal recommendation between all style guides.

Linters like ESLint and JSHint all warn us about the use of the == and != operators.

eval()

eval is a function that takes a string of JavaScript code and runs it.

For instance, we can write:

eval("console.log('hello')");

to log 'hello' in the console log.

The eval function isn’t the only function that runs JavaScript code from a string.

The setTimeout and setInterval functions also takes a function and runs that code.

The Function also takes strings with parameters and code and returns a function.

Running code from a JavaScript string is universally considered to be a bad practice because of the security issues from running code that can potentially be run from any source.

Also, JavaScript code inside a string can’t be optimized for performance with JavaScript engines.

Therefore, we shouldn’t use the eval functuon or Function constructor.

Also, we should use setTimeout and setInterval with actual code instead of a string.

For instance, we use it properly by writing:

setTimeout(() => {
  document.body.style.background = 'red';
}, 50);

setInterval(() => {
  document.title = `the tiome is ${new Date()}`;
}, 1000);

We run our code in a callback instead of running code from a string.

Conclusion

We should use === and !== for comparisons.

Also, we shouldn’t run code from a string.

Categories
JavaScript Best Practices

Maintainable JavaScript — App Logic and Primitive Value Check

Creating maintainable JavaScript code is important if want to keep using the code.

In this article, we’ll look at the basics of creating maintainable JavaScript code by looking at separating app logic from event handling.

Also, we look at how to check for primitive values.

Event Object

When we’re handling events, we should keep the event object in the event handler only.

This will reduce coupling in our code.

So if we need to use something from the event object, we keep them in the event handler.

For instance, we can write:

function onclick(event) {
  const {
    clientX,
    clienty,
    preventDefault,
    stopPropagation
  } = event;

  preventDefault();
  stopPropagation();
  showPopup(clientX, clienty);
}

function showPopup(x, y) {
  const popup = document.getElementById("popup");
  popup.style.left = `${x}px`;
  popup.style.top = `${y}px`;
  popup.className = "popup";
}

const button = document.querySelector('button');
button.addEventListener('click', onClick);

We called the preventDefault and stopPropagation methods in from the event object in the event handler.

This is much better than passing the event object and call it in that function.

We don’t want our app logic code and event handling code to mix.

This way, we can change the app logic and event handling code independently.

And we can call the app logic code directly.

So we can write something like:

showPopup(10, 10);

It also makes testing a lot easier since we can call showPopup directly to test it.

Avoid Null Comparisons

A common problematic pattern in JavaScript code is testing variables against null .

For instance, we may have code like:

function process(items) {
   if (items !== null) {
     items.sort();
     items.forEach((item) => {
       //...
     });
   }
 }

We have the process function that checks the items against null .

Then we assume items is an array and call sort and forEach on it.

Comparing against null doesn’t mean we made sure that it’s an array.

We only know that it’s not null .

We can improve our value comparison so that we can actually check for what we want.

Detecting Primitive Values

There’re a few types of primitive values in JavaScript.

They include bigints, strings, numbers, booleans, null and undefined .

The typeof operator is the best one for checking for types of primitive values.

We just need to pass in the expression we want to check as the operand.

For strings, typeof returns 'string' .

For numbers, typeof returns 'number' .

For boolean, typeof returns 'boolean' .

For bigints, typeof returns 'bigint' .

And for undefined , typeof returns 'undefined' .

The basic syntax for typeof is:

typeof variable

We can also write:

typeof(variable)

However, the 2nd way makes tyepof looks like a function.

Therefore, we should use it without parentheses.

We can code defensively bu using the tupeof operator to check the type of the value of the expression.

For instance, we can write:

if (typeof name === "string") {
  let part = name.substring(3);
}

to check a string.

We can write:

if (typeof count === "number") {
  setCount(count);
}

to check for and set a number.

And:

if (typeof canShowMessage === "boolean" && canShowMessage) {
  showMessage("hello");
}

to check for a boolean and whether it’s true or not.

And we can write:

if (typeof app === "undefined") {
  app = {
    // ....
  };
}

to check for undefined .

Conclusion

We shouldn’t make our app logic depend on the event object.

Also, checking for null isn’t good enough for checking most types of data.