JavaScript Constants With Object.freeze()
Tags: javascript, tutorial, webdev
Published: 2022-03-12 23:40:40

This is a dead-simple observation that I wanted to write about because I almost _never_ see this technique used in the wild.  Nearly every programmer is familiar with the idea of a "constant".  I'm not simply talking about the `const` keyword in JavaScript.  I'm talking about the all-purpose concept of having a variable that is set once - and _only_ once - because, once it's set, that variable's value should never change.  In other words, its value should remain _constant_.

The most common way to do this with modern JavaScript is like so:

```javascript
const SALES_TAX_ALABAMA = 0.04;
const SALES_TAX_FLORIDA = 0.06;
const SALES_TAX_LOUISIANA = 0.0445;
```

Once these variable are instantiated with the `const` keyword, any attempt to reassign them will result in a runtime error.  And this approach... "works", but it can be a bit bulky.  In every script where you want to leverage these variables, you would need to define them all upfront.  And that approach would be unwieldy if these variables are used throughout your codebase.

With modern JavaScript, an alternate approach would be to `export` these values from a single file.  That approach would look like this:

```javascript
// constants.js
export const SALES_TAX_ALABAMA = 0.04;
export const SALES_TAX_FLORIDA = 0.06;
export const SALES_TAX_LOUISIANA = 0.0445;
```

This makes our constants _universal_ and far more accessible throughout our app.  But I would argue that this is still somewhat bulky.  Because every time we want to use any one of these variables inside another file, each variable must be brought into that file with an `import`.

I also find this approach clunky because it doesn't always play nice with the autocomplete feature in our IDEs.  How many times have you been coding when you realize that you need to leverage a constant, like the ones shown above.  But you don't remember, off the top of your head, exactly how those variables are _named_?  So you start typing: _ALA_..., expecting to see the Alabama Sales Tax rate constant pop up.  

But your IDE provides no help in autocompleting/importing the value, because _there is no constant that begins with "ALA"_.  So after you make a few more misguided attempts to pull up the value by typing the name from memory, you eventually give up and _open_ the `constants.js` file so that you can _read through the whole dang file_ for yourself to see exactly how those variables are named.
<br/><br/>




![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h26zoss4tkv4if6t7353.png)
## Objects To The Rescue(???)
This is why I love using JavaScript _objects_ to create namespace conventions.  (In fact, I wrote an entire article about it.  You can read it here: https://dev.to/bytebodger/why-do-js-devs-hate-namespaces-2eg1)

When you save your values as key/value pairs inside _an object_, your IDE becomes much more powerful.  As soon as you type the initial name of the object, and then type `.` nearly any modern IDE will helpfully pull up all of the potential keys that exist inside that object.  

This means that you can restructure your constants file to look like this:

```javascript
// constants.js
export const CONSTANT = {
  SALES_TAX: {
    ALABAMA = 0.04;
    FLORIDA = 0.06;
    LOUISIANA = 0.0445;  
  },
};
```

This supercharges your IDE's autocompletion feature.  _But_... it comes with a downside.  Because, in JavaScript, an object that's been defined with the `const` keyword isn't _really_ a "constant". 

With the example above, the following code will throw a much-needed runtime error:

```javascript
import { CONSTANT } from './constants';

CONSTANT = 'Foo!';
```

It throws a runtime error because `CONSTANT` is defined with the `const` keyword, and we cannot re-assign its value once it's been set.  _However_... this does not necessarily protect _the nested contents_ of the object from being re-assigned.  

So the following code will _NOT_ throw a runtime error:

```javascript
import { CONSTANT } from './constants';

CONSTANT.SALES_TAX.ALABAMA = 0.08;
```

That's really not very helpful, is it?  After all, if any coder, working in any other part of the codebase, can re-assign the value of a "constant" at will, then it's really not a constant at all.
<br/><br/>



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1xkp2ihf4bn6elvlx7v.jpg)
## `Object.freeze()` To The Rescue(!!!)
This is why I use `Object.freeze()` on all of my constants.  (And it's a simple technique that I've rarely ever seen outside of my own code.)

The revised code looks like this:

```javascript
// constants.js
export const CONSTANT = Object.freeze({
  SALES_TAX: Object.freeze({
    ALABAMA = 0.04;
    FLORIDA = 0.06;
    LOUISIANA = 0.0445;  
  }),
});
```

Now, if we try to run this code, it will throw a runtime error:

```javascript
import { CONSTANT } from './constants';

CONSTANT.SALES_TAX.ALABAMA = 0.08;
```

Granted, this is somewhat verbose, because you need to use `Object.freeze()` _on every object_, even those that are nested inside of another object.  In the example above, if you don't freeze the `SALES_TAX` object, you will still be able to reassign its values.
<br/><br/>



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u4s2jw30e7f279eji12b.png)
## A Better Approach
I already know that some devs won't like this approach, because they won't like having to use `Object.freeze()` on every _layer_ of objects in the `constants.js` file.  And that's fine.  There's room here for alternative styles.  But I firmly prefer this method for a couple of simple reasons.

**A Single Constants File**

You needn't use `Object.freeze()` if you want to maintain a _single_ `constants.js` file.  You can just revert to the "traditional" way of doing things, like this:

```javascript
// constants.js
export const SALES_TAX_ALABAMA = 0.04;
export const SALES_TAX_FLORIDA = 0.06;
export const SALES_TAX_LOUISIANA = 0.0445;
```

But I can tell you from decades of experience that it's not too uncommon to open a universal `constants.js` file that has _hundreds_ of variables defined within it.  When this happens, I often find something like this:

```javascript
// constants.js
export const SALES_TAX_ALABAMA = 0.04;
export const SALES_TAX_FLORIDA = 0.06;
export const SALES_TAX_LOUISIANA = 0.0445;
/*
  ...hundreds upon hundreds of other constants 
  defined in this file...
*/
export const ALABAMA_SALES_TAX = 0.04;
```

You see what happened there?  The file grew to be so large, and the naming conventions were so _ad hoc_, that at some point a dev was looking for the Alabama sales tax value, didn't _find_ it, and then created a second variable, with an entirely different naming convention, _for the same value_.

This leads me to my second point:

**Objects Promote a Taxonomic Naming Structure**

Sure, it's _possible_ for a lazy dev to still define the value for the Alabama sales tax rate _twice_ in the same file.  Even when you're using objects to hold those values in a taxonomic convention.  But it's much less likely to happen.  Because, when you're perusing the existing values in the `constants.js` file, it's much easier to see that there's already an entire "section" devoted to sales tax rates.  This means that future devs are much more likely to find the already-existing value.  And if that value doesn't already exist in the file, they're much more likely to add the value _in the correct taxonomic order_.

This also becomes much more logical when searching through those values with our IDE's autocomplete function.  As soon as you type `CONSTANTS.`, your IDE will show you all of the "sub-layers" under the main `CONSTANTS` object and it will be much easier to see, right away, that it already contains a section dedicated to sales tax rates.

**Objects Allow For Variable Key Names**

Imagine that you already have code that looks like this:

```javascript
const state = getState(shoppingCartId);
```

If your naming convention for constants looks like this:

```javascript
// constants.js
export const SALES_TAX_ALABAMA = 0.04;
```

There's then no easy way to dynamically pull up the sales tax rate for `state`.  But if your naming convention for constants looks like this:

```javascript
// constants.js
export const CONSTANT = Object.freeze({
  SALES_TAX: Object.freeze({
    ALABAMA = 0.04;
    FLORIDA = 0.06;
    LOUISIANA = 0.0445;  
  }),
});
```

Then you can do this:

```javascript
import { CONSTANTS } = './constants';

const state = getState();
const salesTaxRate = CONSTANT.SALES_TAX[state.toUpperCase()];
```