Categories
JavaScript Basics

Introduction to Web Workers

To run tasks that take a long time to complete, we need a way to run them in the background.

Web workers are the way to do that. They live in their own context within the browser. Therefore, they don’t have access to the DOM and have their own global variable, self.

A web worker can’t access the window object, but we still use many things that are parts of it like WebSockets and IndexedDB.

In this article, we’ll look at how to create a basic web worker that can send messages between workers and other scripts.


Characteristics of Web Workers

Web workers allow us to send messages between them and other scripts. Non-worker scripts can listen to messages that are emitted by workers by attaching handlers for messages.

To send messages, we use the postMessage() method. The message can be accessed via the Message event’s data property. Message event objects are passed into the event handler of the onmessage handler.

Workers can spawn new workers, as long as they are hosted within the same origin as the parent page. Workers may also use XmlHttpRequest for network I.O, with the exception that the responseXML and channel attributes on XMLHttpRequest always return null.

In addition to dedicated workers, there are also other kinds of workers:

  • Shared workers are workers that can be used by multiple scripts in different windows, IFrames, etc, as long as they’re in the same domain as the worker. They are more complex than dedicated workers in that scripts must communicate via an active port.
  • Service workers act as proxy services that sit between web apps, the browser, and the network. They are intended to create effective offline experiences. They intercept network requests and take appropriate actions based on whether the network is available.
  • Chrome workers are a Firefox-only type of worker that we can use when developing add-ons and want to use workers in extensions.
  • Audio workers provide the ability to direct scripted audio processing to be done inside a web worker context.

Photo by David Siglin on Unsplash


Creating a Simple Dedicated Worker

We can create a dedicated worker by creating a simple worker script and then have other scripts communicate with it.

For example, we can create a dedicated worker that takes values from two inputs and then compute the result by adding them and sending it back to the screen.

First, we create the HTML for getting the input values as follows:

Then, in main.js, we add keyup event handlers to the inputs to send the values to our dedicated worker as follows:

We also add the onmessage event handler to receive the message from the worker, which we’ll add to worker.js to get the message back from it in the following piece of code:

worker.onmessage = e => {  
  result.textContent = e.data;  
};

Finally, we create the worker code in worker.js as follows:

The code above gets the message from main.js, where we called postMessage. Whatever we passed to postMessage will be in the e parameter and we can get the data from there.

Then, in this file, we get the two numbers we passed in with the data property on the first line of the function.

We then check if both numbers are numbers and if they are, we add them together and then send the sum back to main.js with postMessage after we compute the sum and put the result in the workerResult string.

In the end, we should get something like the following:

As long as the dedicated worker script is in the same domain as our other scripts, it’ll run.

The Worker constructor also takes an option object with the following options:

  • type: A string specifying the type of worker to create. The value can be classic or module and defaults to classic.
  • credentials: A string specifying the type of credentials to use for the worker. The value can be omit (no credentials required), same-origin, or include. If it’s not specified, the type is class, then omit is the default.
  • name: A string specifying the identifying name for the DedicatedWorkerGlobalScope representing the scope of the worker. It’s mainly used for debugging.

The constructor will throw a SecurityError if it’s not allowed to start workers, like if the URL is invalid or the same-origin policy is violated.

  • NetworkError is raised if the MIME type of the worker script is incorrect.
  • SyntaxError is raised is the worker’s URL can’t be parsed.

Creating dedicated workers is simple. They let us run background tasks. Communication between workers and other scripts is by sending messages back and forth.

Web workers have their own context so they can’t use the window object or access the DOM.

Categories
TypeScript

Introduction to TypeScript Interfaces — Object Literals and Function Types

The big advantage of TypeScript over plain JavaScript is that it extends the features of JavaScript by adding functionality that ensures the type safety of our program’s objects. It does this by checking the shape of the values that objects take on.

Checking the shape is called duck typing or structural typing. Interfaces are one way to fill the role naming data types in TypeScript. It’s very useful for defining contracts within our code in TypeScript programs. In the last article, we looked at how to define a TypeScript interface and adding required and optional properties to it. In this article, we’ll continue from the previous article and look at other properties of TypeScript interfaces.

Excess Property Checks

Object properties get extra checks when they’re being assigned to a variable with the type designated by an interface. This also applies to object literals that we pass into functions as arguments. For example, the following code wouldn’t be compiled by the TypeScript compiler and give us an error:

interface Person{
  name: string
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}`);
}

greet({ name: 'Joe', foo: 'abc' });

The excess property check done by the TypeScript compiler will reject the code since we have an extra foo property that isn’t defined in the Person interface, so add it in the object in the parameter would fail because of TypeScript’s excess property checks for object literals. Assigning the same object literal to a variable will also fail. For example, if we have the following code:

interface Person{
  name: string
}
const greet = (person: Person) => {
  console.log(`Hello, ${person.name}`);
}
const person: Person = { name: 'Joe', foo: 'abc' };
greet(person);

We would get the error “Type ‘{ name: string; foo: string; }’ is not assignable to type ‘Person’. Object literal may only specify known properties, and ‘foo’” if we try to compile the code with the TypsScript compiler or look at the code at a text editor that supports TypeScript. However, we can use the type assertion operator as to designate the type of the object literal as we like it. So if we’re sure that the object literal if of the type Person even though it has a foo property in it, we can write the following code:

interface Person{
  name: string
}
const greet = (person: Person) => {
  console.log(`Hello, ${person.name}`);
}
const person: Person = { name: 'Joe', foo: 'abc' } as Person;
greet(person);

With the code above, the TypeScript compiler won’t complain of any issues. It’ll just assumes that the object literal is of type Person even though it has a foo property. However, we do have some properties that are dynamic or may only sometimes appear, we can also add a dynamic property to our TypeScript interfaces like in the following code:

interface Person{
  name: string,
  [prop: string]: any
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}. ${person.age ? `You're ${person.age} years old.` : ''}`);
}

const person: Person = { name: 'Jane', age: 20 };
greet(person);

In the code above, we added:

[prop: string]: any

to our Person interface. The line above means that the type Person can have any other property other than name . The property name is a string, which is the case for the dynamic property names in JavaScript, and these dynamic properties can take on any value since specified the any type for the dynamic property. As we can see, we have the following line:

const person: Person = { name: 'Jane', age: 20 };

where our object literal has the age property but it’s not explicitly defined in our interface definition. This is because we have the dynamic property after the name property. The [prop: string] is called the index signature.

We can also get around the excess property check for object literals by assigning a variable to another variable. For example, if we have the following code:

interface Person{
  name: string
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}. ${person.age ? `You're ${person.age} years old.` : ''}`);
}

const person: Person = { name: 'Jane', age: 20 };
greet(person);

which wouldn’t compile and run because of the excess property check, we can get around it by assigning the person constant to a new variable or constant that doesn’t have a type designated to it like we do below:

interface Person{
  name: string
}

const greet = (person: Person) => {
  console.log(`Hello, ${person.name}`);
}

const person = { name: 'Jane', age: 20 };
greet(person);

The person constant doesn’t have a type designated to it so the excess property check for object literals won’t be run.

The excess property check is recommended to be enforced for simple objects like the ones we have above. For more complex, dynamic objects, we can use the ways we outline above to get around the checks to get the code running. However, do be aware that most excess property errors are actually typos in our code, so they’re legitimate bugs that should be fixed.

Photo by Max Baskakov on Unsplash

Function Types

With TypeScript interfaces, we can also define the signature of functions by designating the data type for each parameter and the return type of the function. This prevents us from passing in parameters that have the wrong data type or forgetting to pass in arguments into our function calls, and also ensures that our function always have the same return type and we won’t be returning things that we don’t expect in our code.

We can define a interface for designating the parameter and return data types of our function, and the function signature like we do in the code below:

interface GreetFn{
  (name: string, age: number): string
}

const greet: GreetFn = (name: string, age: number) => {
  return `Hello, ${name}. You're ${age} years old`;
}

console.log(greet('Jane', 20));

The code above has the function greet that follows the function signature defined on the left side of the colon in the GreetFn interface and the return data type on the right side of the interface, so the code will run and produce output from the console.log statement in the last line. We should get ‘Hello, Jane. You’re 20 years old’. If we designate our greet function with the type GreetFn but our function signature or return type stray away from the ones designated in the GreetFn interface then we’ll get errors. For example, if we have:

interface GreetFn{
  (name: string, age: number): string
}
const greet: GreetFn = (name: string, age: number, foo: any) => {
  return `Hello, ${name}. You're ${age} years old`;
}
console.log(greet('Jane', 20));

Then we’ll get the error message “Type ‘(name: string, age: number, foo: any) => string’ is not assignable to type ‘GreetFn’.(2322)“ since our parameter list doesn’t match the signature listed in the interface. Likewise, if our function’s return type doesn’t match the one we defined in the interface, we’ll also get an error. For example if we have the following code:

interface GreetFn{
  (name: string, age: number): string
}
const greet: GreetFn = (name: string, age: number) => {
  return 0;
}
console.log(greet('Jane', 20));

We’ll get the error “Type ‘(name: string, age: number) => number’ is not assignable to type ‘GreetFn’. Type ‘number’ is not assignable to type ‘string’.” This means that the greet function must return a string since we specified that the type of the greet function is GreetFn .

Function parameters are checked one at a time, so the TypeScript compiler infers the position of the type of a parameter by its position even though no types are designated by us when we define our function. For example, the following will still work even though we didn’t specified the type of our parameters explicitly:

interface GreetFn{
  (name: string, age: number): string
}
const greet: GreetFn = (name, age) => {
  return `Hello, ${name}. You're ${age} years old`;
}
console.log(greet('Jane', 20));

If we pass in something with the wrong data type according to the interface we defined like in the code below, we’ll get an error:

interface GreetFn{
  (name: string, age: number): string
}
const greet: GreetFn = (name, age) => {
  return `Hello, ${name}. You're ${age} years old`;
}
console.log(greet('Jane', ''));

When we try to compile the code above, we’ll get the error “Argument of type ‘“”’ is not assignable to parameter of type ‘number’.(2345)“. This means that TypeScript is smart enough to infer the type by its position. Type inference is also done for the return type, so if we write the following code:

interface GreetFn{
  (name: string, age: number): string
}
const greet: GreetFn = (name, age) => {
  return 0;
}
console.log(greet('Jane', 20));

Then we’ll get the error “Type ‘(name: string, age: number) => number’ is not assignable to type ‘GreetFn’. Type ‘number’ is not assignable to type ‘string’.(2322)” so the code won’t compile.

Excess property checks for object literals are useful since it’s much harder for us to add wrong properties or typos into our code when we’re assigning object literals or passing them in as arguments of functions. We can get around it with type assertion or assigning to a variable with different types or no types. We can also define interfaces for functions to define the expected parameters for a function and also the expected return type for them.

Categories
JavaScript JavaScript Basics

Handy JavaScript Shorthands

With the latest versions of JavaScript, the language has introduced more syntactic sugar. In this article, we’ll look at handy shortcuts that are easy to read from versions of JavaScript new and old.

In this article, we’ll look at the ternary operator, declaring multiple variables, arrow functions, default parameter values, and more.

Ternary Operator

We can write an if...else statement in a concise way by using the ternary operator.

Instead of writing:

const x = 20;
let grade;

if (x >= 50) {
  grade = "pass";
} else {
  grade = "fail";
}

We can write:

const x = 20;
let grade = (x >= 50) ? "pass" : "fail";

They both check if x is bigger than or equal to 50 then assign the string 'pass' if it’s true and fail otherwise.

We can also write nested if statement with the ternary operator as follows:

const x = 20;
let grade = (x >= 50) ? "pass" : (x >= 25) ? "good fail" : 'bad fail';

This is the same as:

const x = 20;
let grade;
if (x >= 50) {
  grade = "pass";
} else {
  if (x >= 25) {
    grade = "good fail";
  } else {
    grade = "bad fail";
  }
}

Setting Default Value

We can set a default value if a variable is falsy by writing:

let x;
let y = x || 10;

This is the same as:

let x;
let y;
if (x === undefined || x === null || x === 0 || x === '' || isNaN(x)) {
  y = 10;
}

Because x || 10 means that if x is falsy, which means that x is undefined, null, 0, empty string, or NaN, then we assign 10 to y, which is the same as:

if (x === undefined || x === null || x === 0 || x === '' || isNaN(x)) {
  y = 10;
}

Shorthand for Declaring Multiple Variables

We can declare multiple variables by writing:

let x = y = z = 5;

This is the same as writing:

let x = 5;
let y = 5;
let z = 5;

It works by first assigning 5 to z, then the value of z to y, and finally the value of y to x .

If Truthy Shorthand

A shorthand for checking if something is truthy is JavaScript, which is anything other than undefined, null, 0, empty string, or NaN, is the following:

if (x){
 console.log('x is truthy')
}

The code above checks if x is truthy, then we execute the console.log if it is.

For…Of Loop Shorthand

Since ES6, we can use the for...of loop to loop through variables in an array or array-like object, which includes things like Maps, Sets, the arguments object, generators, iterators, and any object with the [Symbol.iterator] method.

We can write:

let fruits = ['apple', 'orange', 'grape'];
for (let fruit of fruits) {
  console.log(fruit);
}

This is cleaner than using a regular for loop with indexes, and it also works other iterable objects. For example, we can use it with generators:

let fruits = function*() {
  yield 'apple';
  yield 'orange';
  yield 'fruits';
}

for (let fruit of fruits()) {
  console.log(fruit);
}

Photo by Robin Mantz on Unsplash

Array.forEach

We can use the Array.forEach method to loop through an array, although it’s slower than loops.

To use it, we can write something like the following code:

let fruits = ['apple', 'orange', 'grape'];

fruits.forEach((fruit, index) => console.log(fruit));

Decimal Base Exponents

We can specify exponential numbers instead of writing out the whole number out with all the trailing zeroes.

For example, if we have:

1e0

for 1

1e1

for 10

1e2

for 100

1e3

for 1000 and so on.

Digit Separators

The latest browsers let us use an underscore to separate digits to make them easy to read. For example, we can write

100_000_000

for 100 million. Underscores can be placed anywhere we choose.

Object Property Shorthand

Instead of writing:

const foo = 1,
  bar = 2;
const obj = {
  foo: foo,
  bar: bar
};

We can write:

const foo = 1,
  bar = 2;
const obj = {
  foo,
  bar
};

The 2 pieces of code are exactly the same.

Arrow Functions

If an arrow function is only one line, then we don’t need the braces and we can return a value from it without using the return keyword.

For example:

() => 1

is the same as:

() => {
  return 1
}

We can use arrow functions if we don’t care about the value of this since arrow functions don’t change the this value inside the function.

Default Parameter Values

We can specify default parameter values in ES6. For example, we can write:

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

This is the same as:

const add = (a, b) => {
  if (typeof a === 'undefined') {
    a = 1;
  }

  if (typeof b === 'undefined') {
    b = 1;
  }
  return a + b
}

The shorthands above are mostly from ES6. This version of JavaScript provides lots of handy shortcuts which lets us write code easier and reading just as easy.

for...of loop is very useful since it can loop through arrays and array-like objects. No other loops can do that.

Digit separators are more recent and available in the latest browsers only.

Categories
Bootstrap HTML

Bootstrap 5 — Extending Input Groups

Bootstrap 5 is in alpha when this is written and it’s subject to change.

Bootstrap is a popular UI library for any JavaScript apps.

In this article, we’ll look at how to style input groups with Bootstrap 5.

Multiple Inputs

Input groups can have more than one input added inside it.

For example, we can write:

<div class="input-group">  
  <span class="input-group-text">First and last name</span>  
  <input type="text" class="form-control">  
  <input type="text" class="form-control">  
</div>

to add first and last name inputs in one input group.

Multiple Addons

An input group can have multiple addons added to them.

For example, we can write:

<div class="input-group mb-3">  
  <span class="input-group-text">$</span>  
  <span class="input-group-text">0.00</span>  
  <input type="text" class="form-control">  
</div>
<div class="input-group">  
  <input type="text" class="form-control">  
  <span class="input-group-text">$</span>  
  <span class="input-group-text">0.00</span>  
</div>

We have 2 spans with the input-group-text class to add 2 input addons.

Button Addons

Buttons can be added as input group addons.

For example, we can write:

<div class="input-group mb-3">  
  <button class="btn btn-outline-secondary" type="button">Button</button>  
  <input type="text" class="form-control" placeholder="name">  
</div>

We added a button straight into the input-group to use it as an addon.

We can also add a button to the right side:

<div class="input-group mb-3">  
  <input type="text" class="form-control" placeholder="name">  
  <button class="btn btn-outline-secondary" type="button" id="button-addon2">Button</button>  
</div>

Also, we can add more than one addon.

For instance, we can write:

<div class="input-group mb-3">  
  <button class="btn btn-outline-secondary" type="button">Button</button>  
  <button class="btn btn-outline-secondary" type="button">Button</button>  
  <input type="text" class="form-control" placeholder="name">  
</div>
<div class="input-group">  
  <input type="text" class="form-control" placeholder="name">  
  <button class="btn btn-outline-secondary" type="button">Button</button>  
  <button class="btn btn-outline-secondary" type="button">Button</button>  
</div>

Buttons with Dropdowns

Dropdowns can be added as input group addons.

The Bootstrap JavaScript files are required for the dropdowns.

For example, we can write:

<div class="input-group mb-3">  
  <button class="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">fruit</button>  
  <ul class="dropdown-menu">  
    <li><a class="dropdown-item" href="#">apple</a></li>  
    <li><a class="dropdown-item" href="#">orange</a></li>  
    <li><a class="dropdown-item" href="#">lemon</a></li>  
    <li><hr class="dropdown-divider"></li>  
    <li><a class="dropdown-item" href="#">grape</a></li>  
  </ul>  
  <input type="text" class="form-control">  
</div>

to add a dropdown to the left side.

We add the dropdown-toggle class to create the toggle.

And we have the data-toggle attribute to make it a toggle.

To add one to the right side, we can write:

<div class="input-group mb-3">  
  <input type="text" class="form-control">  
  <button class="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">fruit</button>  
  <ul class="dropdown-menu">  
    <li><a class="dropdown-item" href="#">apple</a></li>  
    <li><a class="dropdown-item" href="#">orange</a></li>  
    <li><a class="dropdown-item" href="#">lemon</a></li>  
    <li>  
      <hr class="dropdown-divider">  
    </li>  
    <li><a class="dropdown-item" href="#">grape</a></li>  
  </ul>  
</div>

And we can add a dropdown to both sides:

<div class="input-group mb-3">  
  <button class="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">fruit</button>  
  <ul class="dropdown-menu">  
    <li><a class="dropdown-item" href="#">apple</a></li>  
    <li><a class="dropdown-item" href="#">orange</a></li>  
    <li><a class="dropdown-item" href="#">lemon</a></li>  
    <li>  
      <hr class="dropdown-divider">  
    </li>  
    <li><a class="dropdown-item" href="#">grape</a></li>  
  </ul>  
  <input type="text" class="form-control">  
  <button class="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">fruit</button>  
  <ul class="dropdown-menu">  
    <li><a class="dropdown-item" href="#">apple</a></li>  
    <li><a class="dropdown-item" href="#">orange</a></li>  
    <li><a class="dropdown-item" href="#">lemon</a></li>  
    <li>  
      <hr class="dropdown-divider">  
    </li>  
    <li><a class="dropdown-item" href="#">grape</a></li>  
  </ul>  
</div>

Segmented Buttons

We can make the dropdown button segmented.

To do that, we can write:

<div class="input-group mb-3">  
  <button type="button" class="btn btn-outline-secondary">fruit</button>  
  <button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown">  
    <span class="sr-only">Toggle Dropdown</span>  
  </button>  
  <ul class="dropdown-menu">  
    <li><a class="dropdown-item" href="#">apple</a></li>  
    <li><a class="dropdown-item" href="#">orange</a></li>  
    <li><a class="dropdown-item" href="#">lemon</a></li>  
    <li>  
      <hr class="dropdown-divider">  
    </li>  
    <li><a class="dropdown-item" href="#">banana</a></li>  
  </ul>  
  <input type="text" class="form-control">  
</div>

We add the split toggle button by adding 2 buttons side by side.

We have the dropdown-toggle and dropdown-toggle-split classes to add the split dropdown button.

Also, we can put the split button and dropdown on the right side:

<div class="input-group mb-3">  
  <input type="text" class="form-control">  
  <button type="button" class="btn btn-outline-secondary">fruit</button>  
  <button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown">  
    <span class="sr-only">Toggle Dropdown</span>  
  </button>  
  <ul class="dropdown-menu">  
    <li><a class="dropdown-item" href="#">apple</a></li>  
    <li><a class="dropdown-item" href="#">orange</a></li>  
    <li><a class="dropdown-item" href="#">lemon</a></li>  
    <li>  
      <hr class="dropdown-divider">  
    </li>  
    <li><a class="dropdown-item" href="#">banana</a></li>  
  </ul>  
</div>

Conclusion

We can add multiple addons, inputs, and dropdowns to an input group.

To make the dropdown display, we need the JavaScript files.

Categories
JavaScript JavaScript Best Practices

JavaScript Best Practices — Modules and Identifiers

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

Minimum and Maximum Identifier Lengths

We should make identifier descriptive so they definitely shouldn’t be too short.

It also shouldn’t be too long.

If it’s too short, it’s probably not descriptive enough.

If it’s too long, then it has redundant information and takes up too much space.

So we shouldn’t write:

const x = 5;

since we don’t know what x is.

But we can write something like const numApples = 1; .

Location of Arrow Function Bodies with Implicit Returns

We should put arrows in a logical location.

For instance, we can write:

(foo) => bar;

or

(foo) => (bar);

or

(foo) => bar => baz;

They are conventional and logical.

It’s better than these examples:

(foo) =>  
  bar;

or:

(foo) =>  
  (bar);

The first examples are more compact than these ones.

Default Imports

We’ve have a default export for a default import to work.

For instance, we should write:

foo.js

export default function () { return 100 }

Then we should import it by writing:

import foo from './foo';

For CommonJS modules, we can write:

foo.js

module.exports = function () { return 100 }

Then we can import it by writing:

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

Repeated Imports and Exports

We can only have one default export in each module.

For instance, we can’t write:

export default class Foo { /*...*/ } 
export default class Bar { /*...*/ }

We have to remove one of them.

If we remove one:

export default class Foo { /*...*/ }

Then we can write:

import Foo from './foo';

Export Statements that Come Before Other Statements

We shouldn’t have export statements that come before other statements.

For instance, we don’t want to fix statements like:

const bool = true
export default bool
const str = 'foo'

Moving all exports to the bottom would make it easier to read.

Use of File Extension within the Import Path

When we write imports, we don’t need an extension for JavaScript files.

For instance, instead of writing:

import bar from './foo/bar.js';

We write:

import bar from './foo/bar';

However, we should put the extension for JSON import.

For instance, we can write:

import bar from './bar.json';

We may also do the same thing for JavaScript files that don’t have the .js extension.

For instance, we can write:

import Component from './Component.jsx';

for JSX files.

Putting Imports First

We should put import statements first.

For instance, we should put them at the top.

Instead of writing:

import foo from './foo'
doSomething(foo)
import bar from './bar'

We write:

import foo from './foo';  
import bar from './bar';
doSomething(foo);

Grouping Exports

We can group exports together to make finding them easier.

For instance, we can write:

const first = 'foo';  
const second = 'bar';
export {  
  first,  
  second,  
}

With CommonJS modules, we can write:

const test = {};  
test.foo = true  
test.bar  = true
module.exports = test;

We also grouped the exports together.

Named Exports

We can import named module members in various ways.

Given that we have:

export const foo = "foo"

We can write:

import { foo } from './foo'

Also, we can write:

export { foo as bar } from './foo'

to export it as bar .

We can also import it as bar:

import { foo as bar } from './foo'

Newline After Import

After a group of imports, we can put a new line to separate the imports from the rest of the code.

For instance, we can write:

import defaultExport from './foo'  
import { bar } from 'bar'  
  
const foo = 'bar';

or:

const foo = require('./foo')  
const bar = require('./bar')  
  
const baz = 1;

for CommonJS modules.

No Absolute Paths

We shouldn’t use absolute paths for imports because it’ll only work on the current computer’s current folder strucutre.

If we move it anywhere, it won’t work.

So we shouldn’t write:

import f from '/foo';  
import bar from '/some/path';

or:

const f = require('/foo');  
const bar = require('/some/path');

Instead, we write:

import f from './foo';  
import bar from './some/path';

or:

const f = require('./foo');  
const bar = require('./some/path');

Conclusion

We should write our imports and exports properly.

Never import with absolute paths.

And we should group them together.

Identifier lengths shouldn’t be too long or short.