JavaScript

Last edited 44 seconds ago by System Writer
Kanopi maintains a that is used across all Kanopi projects. It exposes several different configs and engineers should opt-in to the config that best fits the project. We also maintain a that works well for most of our projects.
As far as JavaScript documentation goes, we conform to the and those standards are enforced by Kanopi’s eslint config.
Standardizing the way we structure our JavaScript allows us to collaborate more effectively with one another. Using intelligent design patterns improves maintainability, code readability, and even helps to prevent bugs.

Writing Modern JavaScript

It’s important we use language features that are intended to be used. This means not using deprecated functions, methods, or properties. Whether we are using plain JavaScript or a library, we should not use deprecated features. Using deprecated features can have negative effects on performance, security, maintainability, and compatibility.
On all new projects you should be using up to date JavaScript methodologies combined with a build process tool like to ensure browser compatibility. This allows us to utilize modern techniques while being certain our code will not break in older systems. The have this functionality built in.
Some older projects that have not yet been upgraded may not have the capability to use the most modern techniques, but it is still important to have processes in place that allow us to grow the technology stack as a project matures. In these cases, you should still follow best practice recommendations even if the newest patterns are not yet available to you.
Using Classes
Before ES6, classes in JavaScript were created by building a constructor function and adding properties by extending the prototype object. This created a fairly complex way to extend classes and deal with prototypal inheritance. Modern techniques allow us to create and extend classes directly and write cleaner code:
class BasicExample {
constructor(el) {
super(); // if you're extending
}

init() {
console.log('Hello world.');
}
}
Classes in modern JavaScript offer a nicer syntax to access the standard prototypal inheritance we’ve already had for a while, but can also help guide the structure of componentized code. When deciding whether or not to use a Class, think of the code you’re writing in the greater context of the application.
Classes will not always be the answer for creating modular code in your application, but you should consider them when you need to create discrete components or when those components need to inherit behaviors from other components, while still functioning as a single unit. For example, a utility function that searches a string for text may not be a good utilization of Classes, but an accordion menu with children components would.
Using Arrow Functions
Arrow functions are a great way to slim down easily executable code blocks. When using this style of function be sure not to over engineer a simple action just to make it smaller. For example, this is a good use of a simple multiline function being compressed into a single line:
Multi-line:
const init = (msg) => {
console.log(msg);
};
Single line:
const init = (msg) => console.log(msg);
This is a very simple function, so compressing it down into a single line won’t cause any readability issues. However, the more complicated this function gets, the less likely it should be on a single line.
Even though single argument arrow functions don’t require parenthesis around the argument itself, it is best to include the parenthesis for improved readability and scalability of the function.
Something important to remember is that arrow functions are not always the answer. Their release addressed a very specific problem many engineers were facing with preserving the context of this. In a traditional function this is bound to different values depending on the context it is called. With arrow functions, it is bound to the code that contains the arrow function. Because arrow functions also have syntax benefits, as a general rule, use arrow functions unless you need a more traditional treatment of this (like in an event listener).
Concatenating Strings and Templating
When dealing with strings in JavaScript, it is very common to need some form of concatenation along the way. Before ES6 we were concatenating string with the + operator:
/* eslint-disable */
var first = 'hello';
var last = 'world';
var msg = 'I said, "' + first + ' ' + last + '" to the crowd.';
Modern techniques give us something called, “template literals”, which let us concatenate strings in a much more straightforward manner utilizing the back tick and some basic templating:
const first = 'hello';
const last = 'world';
const msg = `I said, "${first} ${last}," to the crowd.`;
Destructuring Arrays and Objects
Destructuring is a JavaScript technique that allows you to easily assign values and properties from arrays and objects into specific variables. This direct mapping affords an easy way to access data from objects and arrays in a more convenient syntax.
The old way:
const arr = [1, 2, 3, 4];
const a = arr[0];
const b = arr[1];
const c = arr[2];
const d = arr[3];
The new way:
const [a, b, c, d] = [1, 2, 3, 4];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(d); // 4
Use destructuring whenever possible to slim down your code and improve overall readability.
Componentizing Your Code
Keeping different bits of functionality in your code reasonably separated is important to building and maintaining a scalable system over time. In the past we’ve had to accomplish this with our build systems leaning on concatenation as an abstraction layer to the project. Modern JavaScript techniques allow us to utilize statements to break apart and reassemble your code into consumable chunks.
When you’re building your project, be sure to lean on imports to wire your application together. As of right now, we do still need a build system to process the code so it can be consumed by a browser, but using this modern technique will make sure our code is well structured as the project ages. You can also use import statements to load parts of an external library you may need for any given component. The code below will give you an example:
// Only loading in the map method from lodash, because that's all we need!
import map from 'lodash/map';
This also allows you to build one component and import it into another.
It’s also worth noting that named exports can be imported by wrapping the exported function within curly braces:
import { example } from 'example/lib';
This is only possible if the exported component is a named export like so:
export const example = 66;

Modules

When creating your own modules be sure to think about how it should be used by others. Luckily ES6 modules makes this a simple task.
There are many ways you can export a module, but typically exposing specific functions and/or data structures through an ES6 module is the preferred way.
// datastructure.js
// private variable to the module
const data = {};

// private function to the module
const process = (value) => {
// complex logic
return value;
};

// the two functions below are public
export const getData = (field) => {
return process(data[field]);
};

export const addData = (field, value) => {
data[field] = value;
};
In the module above only two functions are being exposed, everything else is private to the module, therefore this module can be used as following.
import { addData, getData } from './datastructure';

addData('key', 'myvalue');

const value = getData('key');
Avoid using classes unless there’s a good reason to. Consider the following example:
import Module from './mymodule';
/* Module is a ES6 class */
new Module('.element-selector', {
/* options */
onEvent1: () => {},
onEvent2: () => {},
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.