RxJS Subjects in Depth

RxJS Subjects in Depth

In this article, I want to explore the topic of RxJS’s implementation of Subjects, a utility that is increasingly getting awareness and love from the community. In the past, I have used Subjects in a variety of ways, but sometimes not fully...

In this article, I want to explore the topic of RxJS’s implementation of Subjects, a utility that is increasingly getting awareness and love from the community.

In the past, I have used Subjects in a variety of ways, but sometimes not fully understanding what they are internally and what are the main differences with Observables.

This is what the article will cover:

  • What is a Subject?
  • Multicasting and Unicasting
  • Other types of Subject: AsyncSubject, ReplaySubject, and BehaviorSubject
What is a Subject?

Let’s start with a simple question: what is a Subject?
According to Rx’s website:

A Subject is a special type of Observable that allows values to be multicasted to many Observers.
Subjects are like EventEmitters.

If this is unclear, hang on, by the end of the article you’ll have a much clearer understanding of what a Subject is and all the way you can make use of them.

The definition stated by the Rx documentation initially struck me: in fact, I always thought of Subjects as purely a way to both pull and push values using streams. It turns out, I didn’t really know them full well even after having used them daily for about 5 years.

Subject

Subject is a class that internally extends Observable. A Subject is both an Observable and an Observer that allows values to be multicasted to many Observers, unlike Observables, where each subscriber owns an independent execution of the Observable.

That means:

  • you can subscribe to a Subject to pull values from its stream
  • you can feed values to the stream by calling the method next()
  • you can even pass a Subject as an Observer to an Observable: as mentioned above, a Subject is also an Observer, and as a such, it implements the methods next, error and complete

Let’s see a quick example:

const subject$ = new Subject();
// Pull values
subject$.subscribe(
  console.log, 
  null, 
  () => console.log('Complete!')
);
// Push values
subject$.next('Hello World');
// Use Subject as an Observer
const numbers$ = of(1, 2, 3);
numbers$.subscribe(subject$);
/* Output below */
// Hello Word
// 1
// 2
// 3
// Complete!
The Internals of a Subject

Internally, every Subject maintains a registry (as an array) of observers. This is how internally a Subject works, in a nutshell:

  • every time a new observer subscribes, the Subject will store the observer in the observers' array
  • when a new item is emitted (i.e. the method next() was called), the Subject will loop through the observers and emit the same value to each one of them (multicasting). The same will happen when it errors or completes
  • when a Subject completes, all the observers will be automatically unsubscribed
  • when a Subject is unsubscribed, instead, the subscriptions will still be alive. The observers’ array is nullified, but it doesn’t unsubscribe them. If you attempt to emit a value from an unsubscribed Subject, it will actually throw an error. The best course of action should be to complete your subjects when you need to dispose of them and their observers
  • when one of the observers is unsubscribed, it will then be removed from the registry
Multicasting

Passing a Subject as an Observer allows to convert the Observable’s behavior from unicast to multicast. Using a Subject is, indeed, the only way to make an Observable multicast, which means they will share the same execution with multiple Observers.

Wait, though: what does sharing execution actually mean? Let’s see two examples to understand the concept better.

Let’s use the observable interval as an example: we want to create an observable that emits every 1000 ms (1 second), and we want to share the execution with all the subscribers, regardless of when they subscribed.

const subject$ = new Subject<number>();
const observer = {
  next: console.log
};
const observable$ = interval(1000);
// subscribe after 1 second
setTimeout(() => {
  console.log("Subscribing first observer");    
  subject$.subscribe(observer);
}, 1000);
// subscribe after 2 seconds
setTimeout(() => {
  console.log("Subscribing second observer");
  subject$.subscribe(observer);
}, 2000);
// subscribe using subject$ as an observer
observable$.subscribe(subject$);

Let’s summarize the snippet above

  • we create a subject called subject$ and an observer which simply logs the current value after each emission
  • we create an observable that emits every 1 second (using interval)
  • we subscribe respectively after 1 and 2 seconds
  • finally, we use the subject as an observer and subscribe to the interval observable

Let’s see the output:

As you can see in the image above, even if the second observable subscribed after 1 second, the values emitted to the 2 observers are exactly the same. Indeed, they share the same source observable.

Another common example that shows the usefulness of multicasting is subscribing to an observable that executes an HTTP request, a scenario that happens often in frameworks such as Angular: by multicasting the observable, you can avoid executing multiple requests and share the execution with multiple subscribers, that will receive the same value.

AsyncSubject

I personally find AsyncSubject the least known type of Subject, simply because I never really needed it, or more likely I didn’t know I could have needed it.

In short, the AsyncSubject will:

  • emit only once it completes
  • emit only the latest value it received
const asyncSubject$ = new AsyncSubject();
asyncSubject$.next(1);
asyncSubject$.next(2);
asyncSubject$.next(3);
asyncSubject$.subscribe(console.log);
// ... nothing happening!
asyncSubject$.complete();
// 3

As you can see, even if we subscribed, nothing happened until we called the method complete.

ReplaySubject

Before introducing a ReplaySubject, let’s see a common situation where normal Subjects are used:

  • we create a subject
  • somewhere in our app, we start pushing values to the subject, but there’s no subscriber yet
  • at some point, the first observer subscribes
  • we expect the observer to emit the values (all of them? or only the last one?) that were previously pushed through the subject
  • … nothing happening! In fact, a Subject has no memory
const subject$ = new Subject();
// somewhere else in our app
subject.next(/* value */);
// somewhere in our app
subject$.subscribe(/* do something */);
// nothing happening

This is one of the situations when a ReplaySubject can help us: in fact, a Subject will record the values emitted and will push to the observer all the values emitted when a subscribed.

Let’s get back to the question above: does a ReplaySubject replay all the emissions or only the latest one?

Well, by default, the subject will replay all the items emitted, but we can provide an argument called bufferSize. This argument defines the number of emissions that the ReplaySubject should keep in its memory:

const subject$ = new ReplaySubject(1);
subject$.next(1);
subject$.next(2);
subject$.next(3);
subject$.subscribe(console.log);
// Output
// 3

There’s also a second argument that can be passed to ReplaySubject in order to define how long the old values should be stored in memory.

const subject$ = new ReplaySubject(100, 250);
setTimeout(() => subject$.next(1), 50);
setTimeout(() => subject$.next(2), 100);
setTimeout(() => subject$.next(3), 150);
setTimeout(() => subject$.next(4), 200);
setTimeout(() => subject$.next(5), 250);
setTimeout(() => {
  subject$.subscribe(v => console.log('SUBCRIPTION A', v));
}, 200);
setTimeout(() => {
  subject$.subscribe(v => console.log('SUBCRIPTION B', v));
}, 400);
  • we create a ReplaySubject whose bufferSize is 100 and windowTime 250
  • we emit 5 values every 50ms
  • we subscribe the first time after 200ms and the second time after 400ms

Let’s analyze the output:

SUBCRIPTION A 1
SUBCRIPTION A 2
SUBCRIPTION A 3
SUBCRIPTION A 4
SUBCRIPTION A 5
SUBCRIPTION B 4
SUBCRIPTION B 5

The subscription A was able to replay all the items, but the subscription B was only able to replay items 4 and 5, as they were the only ones emitted within the window time specified.

BehaviorSubject

BehaviorSubject is probably the most well-known subclass of Subject. This kind of Subject represents the “current value”.

Interestingly, the Combine framework named it CurrentValueSubject

Similarly to ReplaySubject, it will also replay the current value whenever an observer subscribes to it.

In order to use BehaviorSubject we need to provide a mandatory initial value when this gets instantiated.

const subject$ = new BehaviorSubject(0); // 0 is the initial value
subject$.next(1);
setTimeout(() => {
  subject$.subscribe(console.log);
}, 200);
// 1

Whenever a new value is emitted, the BehaviorSubject will store the value in the property value which can also be publicly accessed.

Final Words

Rx Subjects are quite powerful tools, and like any powerful tool in software engineering, they can also be easily abused. The concept of unicasting and multicasting is a striking distinction that you need to take into account when working with Rx.

Understanding how Subjects work internally can be fairly beneficial to avoid common pitfalls and bugs, but also to understand when you need them and when, instead, you don’t.

If you need any clarifications, or if you think something is unclear or wrong, do please leave a comment!

Trick or Unsubscribe in RxJS: a Custom Angular Decorator

Trick or Unsubscribe in RxJS: a Custom Angular Decorator

One of the first concepts of Angular is Functional Reactive Programming via Observables. Angular extensively use Observables through RxJS library that introduces an implementation of the Observable type.

Background

Why Observables might be dangerous for your application? What are the options to reduce the risks? As you may have already guessed I’m going to talk about the “unsubscribe()” and I’ll be honored to present you my custom solution that is saving my lifetime and might save yours.

Introduction to Observable’s world

One of the first concepts of Angular is Functional Reactive Programming via Observables. Angular extensively use Observables through RxJS library that introduces an implementation of the Observable type. I won’t elaborate on the subject of Reactive Programming in the Angular or the RxJS library, I will just cover a few high-level principles.

According to official docs - “Observables are lazy Push collections of multiple values”. Other words to say, it is a data stream - a sequence of any values in time. So, an Observable is some kind of advanced Promise that pushes (resolves) multiple values over time to callbacks instead of only one value.

In order to notify the Observable when to send data and also react to new data in the future, we need to subscribe to it, by simply calling the “subscribe()” method. As I have mentioned above, the Observable is some kind of a stream itself what means that after subscribing to it, its execution will be infinite. And in order to cancel/complete it and “sleep like a baby”, we have simply to call an “unsubscribe()” method. Easygoing, right?

However, here’s the most common mistake, especially among juniors, when a developer simply forgets to unsubscribe from a stream and moves further. And an Observable that isn’t used anymore still would be producing values. That directly leads to tremendous memory leaks and unpredictable behavior of your application in the future.

What are the "advanced" options to unsubscribe?

As I have mentioned above, if you don’t want to shoot yourself in a leg - you always should remember to unsubscribe! The most common place to do it in Angular is inside of “ngOnDestroy” lifecycle hook that is executed by Angular once the component is not used anymore.

This is the easiest solution when you have one or two subscriptions but in the real Angular application, you have dozens of subscriptions. And definitely, it would be tedious each time to unsubscribe “manually”. What to do then? Let’s consider some "advanced" built-in ways of unsubscribing from multiple Observables:

1. Chained subscriptions:

As a Subscription is a Class that essentially has an “unsubscribe()” method, it also has an “add()” method. It allows "adding" one Subscription into another - a child subscription to a parent subscription. Thus, you need to call an unsubscribe() method only once - a parent Subscription unsubscribes all child Subscriptions. Have a look at the example below.

export class HomeComponent implements OnInit, OnDestroy {
  sub: Subscription = new Subscription();

constructor(
private invoicesService: InvoicesService,
private productsService: ProductsService,
private customersService: CustomersService,
) {
}
ngOnInit() {
this.sub
.add(
this.invoicesService.invoices$
.subscribe(invoices => console.log(invoices))
)
.add(
this.productsService.products$
.subscribe(products => console.log(products))
)
.add(
this.customersService.products$
.subscribe(products => console.log(customers))
);
}
ngOnDestroy() {
this.sub.unsubscribe();
}

However, there’s an adverse effect within chaining - in case one of the chained subscriptions completes, e.g. the products$ stream throws an error, then its further descendant, I mean the customers$ stream, won’t be executed. Thus, I’d suggest avoiding chaining.

2. An array of Subscriptions:

Firstly, we create a variable with type “Subscription[]”, e.g. “subscriptions” with initial value as an empty Array. Then we create a setter in order not to wrap manually each Subscription in a “push” construct. Afterward, in the ngOnDestroy lifecycle hook we simply call the forEach() method on our Array and call an unsubscribe() method on each subscription inside of it. Check out the code example:

export class HomeComponent implements OnInit, OnDestroy {

subscriptions: Subscription[] = [];

private set sub (sub: Subscription) {
this.subscriptions.push(sub);
}

constructor(
private invoicesService: InvoicesService,
private productsService: ProductsService,
) {
}

ngOnInit() {
this.sub = this.invoicesService.invoices$
.subscribe(invoices => console.log(invoices));

this.sub = this.productsService.products$
  .subscribe(products =&gt; console.log(products));

}
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
}

3. RxJS “Subject” and “takeUntil” operator:

Firstly, we create a variable/stream, e.g. unsubscribe$ with a new instance of the RxJS Subject. Then inside of the pipe chain of any other stream, we declare the “takeUntil” operator to which we simply pass our unsubscribe$ stream. Afterward, in the ngOnDestroy lifecycle hook, we call next() and complete() callbacks on our Subject. It means that all subscribers automatically stop receiving future values when our Component would be destroyed because our Subject would be completed. Let me provide you with a code example:

export class HomeComponent implements OnInit, OnDestroy {

unsubscribe$: Subject<void> = new Subject();

constructor(
private invoicesService: InvoicesService,
private productsService: ProductsService,
) {
}

ngOnInit() {
this.invoicesService.invoices$
.pipe(
takeUntil(this.unsubscribe$)
)
.subscribe(invoices => console.log(invoices));

this.productsService.products$
  .pipe(
    takeUntil(this.unsubscribe$)
  )
  .subscribe(products =&gt; console.log(products));

}

ngOnDestroy() {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
}

4. RxJS “AsyncPipe”:

This is the last, however, the most reliable, neat and correct built-in option for unsubscribing within Observables. An “AsyncPipe” automatically subscribes to an Observable, returns the latest value it has emitted and also unsubscribes when a Component is destroyed. Thus, we don’t need to do anything. All the cleanup logic for avoiding the memory leaks is done under the hood. It’s amazing! Just take a look at an example below:

export class InvoicesComponent implements OnInit {

invoices$: Observable<Invoice[]>;

constructor(
private invoicesService: InvoicesService,
) {
}

ngOnInit() {
this.invoices$ = this.invoicesService.invoices$;
}
}
<main class="invoices-main">

&lt;mat-table [dataSource]='invoices$ | async'&gt;

....
</mat-table>

<main/>

Why have I come to a custom solution and what are the decorators itself?

The AsyncPipe is reliable and works well, however, very often we have to not just simply subscribe to an Observable and render the output, we need to put some logic in a subscribe() method. Thus, every time we’ll have to repeat the implementation in our Components one of those advanced unsubscribing options mentioned above.

So, after a while, I’ve decided that I don’t want to do a “monkey job” inside of many Components manually. I thought that it would be great to put out all the unsubscribing logic somewhere in one place and just reuse it when I would need, additionally to make my code cleaner and maintainable. And, thanks to the Typescript, I’ve found the right, neat and “Angularish” place - a Decorator. You might already know that Decorators are extensively used throughout an Angular, but if you don’t know what are the Decorators itself and asking yourself what is the magic under the hood, let me explain it very briefly.

In general, the main idea of Decorator is that you can dynamically attach to the object additional functionality. And if to be more precise, in a Typescript, the Decorator is a pure function with arguments that is called by @ sign and can be attached to:

  • Classes;
  • Methods;
  • Properties;
  • Parameters;
  • Accessor.

Just in case, here’s a simple example within a Class:

function Log() {
console.log(arguments);
}

@Log
export class HomeComponent {
...
}
// printed to console:
// {'0': [Function: HomeComponent]}

All in all, Decorators simply help to customize the thing they are attached to at design time. Let’s move further where I would be glad to present and describe my own Decorator for unsubscribing from Observables that I’ve called - “DestroySubscribers”.

My custom @DestroySubscribers() decorator

I’m really delighted with RxJS, but I’ve decided to automate the unsubscribe process and clean my code with the help of a Class Decorator and an “Array of Subscriptions” approach implementation.

Check out the “DestroySubscribers” Decorator itself:

export function DestroySubscribers(params?) {

return function (target) {
params = {
destroyFunc: 'ngOnDestroy',
...params
};
const unsubscribableLike: {subscriptions: Unsubscribable[], unsubscribe: () => void} = {
subscriptions: [],
unsubscribe,
};
const subscriber: string = Reflect.getMetadata('subscription:name', target.prototype, 'subscriber');

Object.defineProperty(target.prototype, subscriber ? subscriber : 'subscriber', {
get: () => unsubscribableLike,
set: subscription => unsubscribableLike.subscriptions.push(subscription),
});

if (typeof target.prototype[params.destroyFunc] !== 'function') {
throw new Error(${target.prototype.constructor.name} must implement ${params.destroyFunc}() lifecycle hook);
}

target.prototype[params.destroyFunc] = ngOnDestroyDecorator(target.prototype[params.destroyFunc]);

function ngOnDestroyDecorator(f) {
return function () {
unsubscribe();
return f.apply(this, arguments);
};
}

function unsubscribe() {
do {
const sub: Unsubscribable = unsubscribableLike.subscriptions.shift();
if ( sub && typeof sub.unsubscribe === 'function') { sub.unsubscribe(); }
} while (unsubscribableLike.subscriptions.length);
}

return target;
};
}

export function CombineSubscriptions(params?) {
return function (target, propertyKey: string | symbol) {
Reflect.defineMetadata('subscription:name', propertyKey, target, 'subscriber');
};
}

As you can see from the code above - the “@DestroySubscribers()” Decorator represents an “Array of subscriptions” approach extended with the “@CombineSubscriptions()” Decorator, and everything is done under the hood now. Let me briefly describe its main code points.

First, I’ve created an object with an empty array for future Subscriptions and custom unsubscribe method in order to have an ability to unsubscribe from all subscriptions at a time manually. Then with the help of reflect-metadata library and “@CombineSubscriptions” Decorator, I‘ve got the current property name from the Class or assign the “subscriber” as a default name and create getter and setter methods. Afterward, I’ve created another version of ngOnDestroy lifecycle hook that firstly unsubscribes from all subscriptions in the array, secondly invokes and returns the original ngOnDestroy method by default or another “destroying function” specified in the passed config to Decorator. That’s it - quite concise and easy to use.

And the decorator's implementation is even simpler. Check it out:

@DestroySubscribers({
destroyFunc: 'ngAfterViewInit',
})
export class HomeComponent implements OnInit, AfterViewInit {

/*
Within the @CombineSubscriptions Decorator, you can choose any custom name that you prefer.
Without the @CombineSubscriptions Decorator, the name by default is 'subscriber'.
*/
@CombineSubscriptions()
private subscriber: Unsubscribable;

constructor(
private invoicesService: InvoicesService,
private productsService: ProductsService,
) {
}

ngOnInit() {
this.subscriber = this.invoicesService.invoices$
.subscribe(invoices => console.log(invoices));

this.subscriber = this.productsService.products$
  .subscribe(products =&gt; console.log(products));

}

/*
This method must be declared, even if it's empty.
Otherwise, the Decorator would throw an Error.
*/
ngAfterViewInit() {
console.log('for unsubscribing');
}
}

The DestroySubscribers Decorator’s main definitions:

  • “subscriber” - a variable that represents the name by default for each subscription and conforms to an Unsubscribable Interface. Each time when you assign a Subscription to the "subscribe" variable - it's auto pushed to the array of Subscriptions under the hood. In addition, if you want to unsubscribe from all subscriptions at a time manually before a Component destroys, you can call an unsubscribe() method on the "subscriber" variable.
  • “@CombineSubscriptions()” Decorator - implement this Decorator in case you want to change the default variable's name("subscriber") of a subscription and use your own custom name, otherwise don't apply it.
  • {destroyFunc: '...' } - add this parameter to the “@DestroySubscribers” Decorator with the name of a hook for auto unsubscribing in case you want to change the default one - "ngOnDestroy" lifecycle hook, otherwise don't apply it. An ability to change the function called when a Component is destroyed gives you an opportunity to use this Decorator not only within an Angular.

The DestroySubscribers Decorator’s implementation steps:

Firstly, you have to annotate the Class with the “@DestroySubscribers()” Decorator.

Secondly, you need to create a variable called “subscriber” by default with the type Unsubscribable or if you want to use your own custom name - simply annotate that variable with “@CombineSubscriptions()” Decorator.

Thirdly, you should just assign to that variable each Subscription that you want to be unsubscribed from when the Component would be no longer in use.

The last thing, you must declare the ngOnDestroy lifecycle hook in a Component even if it's empty because of AOT compilation. Otherwise, the Decorator would throw an Error. In case you change the default lifecycle hook(ngOnDestroy) to another one(ngAfterViewInit) as in an example above, then this method must be declared in a Component, and the ngOnDestroy is obviously optional. I’ve told you, as easy as falling off a log!

Conclusion

All in all, I would like to outline that Decorators itself are nothing to be scared of, but rather are really amazing tools that you should use. They definitely would make your code more reusable, concise and readable!

In addition, thanks to the Angular community over time appeared many different solutions for unsubscribing. All of them are noteworthy and make our everyday lives easier! 

Thanks for reading. If you liked this post, share it with all of your programming buddies!

This post was originally published here

Node vs Angular : Comparing Two Strong JavaScript Technologies

Just from being a simple client-side scripting language, JavaScript has evolved over the years to turn out to be a powerful programming language. Here Node.js is a cross-platform runtime environment while AngularJS is one of the top JavaScript framework. Angular helps the developers to build web applications which are dynamic in nature using HTML template language and following the MVC design pattern.

15 Common Mistakes Developers Made While writing JavaScript Apps

15 Common Mistakes Developers Made While writing JavaScript Apps

Every developer make mistake some time when writing apps. This is a normal part of the development process. In this post, we'll learn 15 Common Mistakes Developers Made While Writing JavaScript Apps hope this post will help you

Common Mistakes Developers Made While Writing JavaScript Apps

Every developer make mistake some time when writing apps. This is a normal part of the development process. There are some mistakes that are made more often than others. It is good to be aware of them so that they can be corrected before it’s made. Once we can do that then we can develop apps a lot easier and become a proficient JavaScript developer since we can develop apps that makes less bugs. We will go through some of the more common bugs that are made so we can avoid them here

1. Confusing the Equals

In JavaScript, single equal, double equal, and tripe equal all have their own meanings. Single equal sign is the assignment operator which assigns the value on the left to the variable to the right. Double equal sign is the equality check operator which checks an object’s content for equality without checking its type. The triple equals operator checks an object’s content and the type for equality.

We use the single equal assignment operator like the following:

let a = 1

This is used for setting variable values, so it’s we use single equal in a place that shouldn’t be used like in:

if (a = 1){ ... }

then we would be assigning 1 to a which is always true . This means that the code inside the if statement always runs. That’s definitely not what we want.

Double equal sign shouldn’t be too too much since it doesn’t check the type of the object when checking for equality, so something like:

1 == '1'

if true . This creates problems because if we are only checking for number 1 and we get string with 1 in it, it’s still true. We want to convert them to the same type and then compare them with === to check for equality to avoid ambiguity. So if we want to compare if all forms of 1 are equal to each other we convert them to number first by using:

Number(1) === Number('1')

1 wouldn’t be equal to '1' is we use === since they are different types. This would make things a lot more clear. If we want to make sure something aren’t equal to each other then we can use !== to check if they don’t have the same content or the same type, so:

1 !== '1'

would be true .

2. Mismatching Brackets

Nested statements and functions in each other means that there’re multiple levels of brackets in each file. Usually, apps are pretty complex so the levels can add up. This means that mismatching brackets is easy if you use a text editor that doesn’t support syntax highlight or check for mismatched brackets. This can easily be avoided with modern text editors such as Visual Studio Code, Atom, and Sublime. If we want to use simpler text editors, then use linters and code formatting tools like ESLint and Prettier to detect these issues. They also let us format code automatically and detect common style issues that may occur like quote style inconsistencies, number of characters in a line, functions that can be shortened, etc.

3. Mismatching Quotes

JavaScript lets us use single quotes, double quotes, and backticks for strings. They are equivalent. However, we should open and close with the same character. So if we open a string with single quote then use single quote to close a string, and if we started with double quotes or backticks, then use those to close the string respectively. Also, special characters like quotation marks have to escape to be included in a string in some cases. If you open a string with single quotes, then if you use single quotes in a string, then you have to escape that to include it in the string. This also applies to double quotes and backticks. If you use double quotes in a double quoted string then you have to escape it. And if you use a backtick in a template string, then you have to escape the backtick character.

4. Parentheses

In if statements, parentheses always have to wrap around the whole condition. For example, something like:

if (x > y) && (y < 10) {...}

won’t work. The correct way to write that statement is to write is:

if ((x > y) && (y < 10)) {...}

if we want to check that both conditions are true.

5. Semicolon

Semicolons are optional at the end of a JavaScript line. However, to make things clear for other people, we should include it so that people know where a line ends. We can use a code linter or formatter like ESLint or Prettier to do this automatically. Lots of text editors also have add-ons to fix it automatically for you.

6. Capitalization

JavaScript code is case-sensitive, so things that have different capitalization will be considered different even if they have the same spelling. Anything that references them also have to be capitalized correctly. For example, getElementsByTagName should always be spelled this way with exact same capitalization. The general convention in JavaScript is that we spell variables and function names with camel case, so we should stick with that for consistency if we define our own variables.

7. Referencing Code That’s not Yet Loaded

It’s important that we reference code that have been defined and loaded before hand in order to use the code. If we are using script tag then before we reference the code, we have to load the code first. So the script tag that references the script with the variable we want to use should come before the script tag that references the script that has the code that we want to reference.

Fortunately, JavaScript modules are much more common nowadays, so that we just have to remember to export the code that you want to use in another module and the import the exported variable in the module that you want to use the variable before referencing it. JavaScript modules solves a lot problems with script tags. Global variables do not have to used much anymore to let different scripts reference the same variable, and also we can make variables private without the use of closures since we only need to export the ones that are needed elsewhere.

8. Variable Names

In JavaScript, there are many reserved words that cannot be used as variable names. They shouldn’t be used as variable names since they collide the name of the reserved words, creating confusion with the JavaScript interpreter. To avoid this we should use names that are descriptive like firstName , lastName , and things like that.

9. Scope

We shouldn’t use global variables whenever possible. This means that all variables we declare should use the let or const keywords for variables and constants respectively. This avoids issues with global variable scope and overwriting global variable data. It also let us avoid errors from assigning to entities that should be constant or assigning things accidentally that are global.

10. Missing or Unexpected Parameters

When functions are called with expected parameters missing, then unexpected results may be returned because you passed in unexpected input to a function.We have to make sure that we pass in the right parameters to a function so that we get the right results. It also a good idea to check for unexpected parameters if the function is used in lots of places like functions that are in libraries so that we can deal with unexpected parameters gracefully like ending the execution of the function when parameters passed in are of an invalid type or null or undefined .

11. Index Errors

JavaScript arrays starts with index 0, so the last index of an array will always be 1 less than the length of the array. When you pass in an index that is beyond the length or it’s invalid like a negative number, we get undefined , so we will get ReferenceErrors if we try to get the invalid entry and do stuff with it.

12. Null and Undefined

null and undefined are things that are often the values of variables. This means that we have to check for these when we are getting something by traversing the nested properties of objects or looping through an array. When we have null or undefined we want to handle them gracefully. This means that we should always check for them. So we if have an object called foo and we want to access deeply nested properties within it, then we should check for null or undefined in each level, like so:

if (foo && foo.bar && foo.bar.baz){ ... }

This way we know that all the properties are defined so that we can get access the baz property. For arrays if we want to access index i , we should check if it’s defined by writing:

if (arr[i]){...}

so that we won’t get unexpected undefined errors.

The optional chaining operator (?. ) is being worked on, so hopefully it will be finalized soon and we can use it to access deeply nested properties in an object and we don’t have to deal with properties being undefined as much anymore. Also, Lodash has a get function, which can try to access deeply properties of arrays and objects and get the value if it exists or return null if there’s anything undefined in the middle of the object’s hierarchy. This is much better than a long chain of undefined or null checks.

13. Confusing Undefined and Null

JavaScript has both undefined and null for non-values. However, there are quite a few differences between the two. undefined means that the variable may have been declared, but nothing is set to it. A variable can also be explicitly set as undefined . The type of an undefined variable, when checking the type with the typeof operator, will get us the type 'undefined'. Functions that don’t return anything returns undefined. On the other hand, null values have to be explicitly set by functions that return null or just set directly to a variable. When we check an object that has the null value set, we’ll find that the type of it is'object' if a variable has the null value.

For this reason, it’s probably easier to stick to undefined whenever we can when we’re setting variable values to non-values. It reduces confusion and we only have to check that the type of a variable is 'undefined' to see whether it’s undefined. That’s less painful than having two checks, for both null and undefined.

To write functions that return undefined, we don’t have to do anything like the following example:

const f = () => {}

To set a variable that was assigned some other value to undefined, we can write:

x = undefined;

To check if a property value is undefine, we can write:

typeof obj.prop === 'undefined'

or

obj.prop === undefined

To check if a variable is undefined, we can write the following code:

typeof x === 'undefined'

A declared variable that hasn’t been assigned anything automatically has the value undefined.

If we have to check for null, then we can write:

obj.prop === null

or

x === null

for variables. We can’t use the typeof operator for checking null because the data type of null is 'object'.

14. Confusing Addition and Concatenation

In JavaScript, the + operator is used for both adding two numbers and concatenating strings together. Because JavaScript is a dynamic language, the operands are all automatically converted to the same type before the operation is applied. For example, if we have:

let x = 1 + 1;

then we get two because they’re both numbers. The + operation was used for addition like we expected. However, if we have the following expression:

let x = 1 + '1';

then we get '11' because the first operand was coerced into a string before the + operation is applied. The + operation was used for concatenation instead of addition. When we use the + operator on multiple variables, this makes knowing the type even harder. For example, if we have:

let x = 1;
let y = 2;
let z = x + y;

as we expect, we get three because x and y are both numbers. On the other hand, if we have:

let x = 1;
let y = '2';
let z = x + y;

then we get '12' because y is a string, so the + operator was used for concatenation instead. To solve this issue, we should convert all the operands to numbers first before using them with the + operator. For example, we should rewrite the above example into the following:

let x = 1;
let y = '2';
let z = Number(x) + Number(y);

The code above will get us 3 as the value of z since we converted them both to numbers with the Number factory function first. The Number function takes in any object and returns a number if it can be parsed into a number, or NaN otherwise. An alternative way to do this is to use the new Number(...).valueOf() function, as we do in the following code:

let x = 1;
let y = '2';
let z = new Number(x).valueOf() + new Number(y).valueOf();

Since new Number(...) is a constructor that creates an object type, we want to use the valueOf function to convert it back to a primitive type to make sure that what we get is a number type. A shorter way to do this is to write:

let x = 1;
let y = '2';
let z = +x + +y;

The + sign in front of a single operand will try to convert the single operand into a number or toNaN if it can’t be converted into a number. It does the same thing as the Number function. We can also convert a variable to a particular type of number. The Number object has a parseInt function to convert an object into an integer and a parseFloat function to convert an object into a floating-point number. parseInt takes the object you want to convert to a number as the first argument. It also takes a radix as an optional second argument, which is the base of the mathematical numeral systems. If the string starts with 0x, then the radix will be set to 16. If the string starts with anything else, then the radix will be set to 10.

We can use them as in the following examples:

let x = 1;
let y = '2';
let z = Number.parseInt(x) + Number.parseInt(y)

Also, we can use the parseFloat function as in the following code:

let x = 1;
let y = '2';
let z = Number.parseFloat(x) + Number.parseFloat(y)

We will get 3 in both of the examples above.

15. Breaking Return Statements Into Multiple Lines

JavaScript closes a statement at the end, so one line code is considered distinct from the other. For example, if we have:

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

we get undefined if we run console.log(add(1, 2)); since we ran the return statement, which ended the function execution, before a + b is run. Therefore, a + b will never be run in this function. To fix this, we either have to put the return statement all in one line or use parentheses to surround what we want to return. For example, we can write:

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

This will log 3 if we run console.log(add(1, 2)); since we are actually returning the computed result in the function. We can also write:

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

This is handy for returning expressions that might be longer than one line. This will also log 3 if we run console.log(add(1, 2));. For arrow functions, we can also write:

const add = (a, b) => a + b

for single-line functions since the return statement is implicit for single-line arrow functions. We can also write:

const add = (a, b) => (a + b)

to get the same thing. This also works for single-line arrow functions.

In JavaScript, if a statement is incomplete, like the first line of:

const power = (a) => {
  const
    power = 10;
  return a ** 10;
}

inside the function then the JavaScript interpreter will run the first line together with the second line to get the full statement. So:

const
  power = 10;

is the same as:

const power = 10;

However, for complete statements like return statements, the JavaScript interpreter will treat them as separate lines. So:

return 
  a ** 10;

is not the same as:

return a ** 10;

Even though JavaScript is a friendly language, it’s still very easy to make mistakes when writing JavaScript code. It’s easy to confuse undefined and null when we aren’t familiar with JavaScript. Because of the dynamic typing nature of JavaScript, operators like the + operator that can do multiple things can easily be converted to a type we don’t expect and produce the wrong result. Also, if statements can be complete on their own, then they shouldn’t be written in their own lines if we want both lines to be together.

Yes, that is all. I hope this tutorial will surely help and you if you liked this tutorial, please consider sharing it with others. Thank you !