DEV Community

Cover image for JavaScript Constants With Object.freeze()

JavaScript Constants With Object.freeze()

Adam Nathaniel Davis on March 12, 2022

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...
Collapse
 
bytebodger profile image
Adam Nathaniel Davis

Code organization: With a little better organization, you don't need to use objects and freeze everything. You can just use the file system and better conventions.

I've not seen, in my IDE, an ability to perform autocompletions based on file structure. But I'll have to play around with that some...

Object freezing: Object freezing is generally there to avoid unwanted mutations, but with JSDocs or TypeScript types, you can avoid those mutations quite easily.

Well, yes and no. My main concern is that I want unwanted mutations to be stopped at runtime. This is one of my big qualms with TS.

I truly appreciate your thoughtful replies!!!

 
bytebodger profile image
Adam Nathaniel Davis

I wrote a whole article about this previously (dev.to/bytebodger/tossing-typescri...). I find TS's type "safety" to be illusory in a frontend/web-based environment. Not asking you to agree with me. If I'm writing Node apps, then I find TS's typing to be much more useful. But when I'm building frontend apps, I feel strongly that it's a false security blanket. Again, not asking you to agree with me on that one. It's just a strong conviction based upon my past experiences.

 
bytebodger profile image
Adam Nathaniel Davis

Because I want to know that, any time an attempt is made to mutate the object, the code will fail. I'm rarely interested in compile time. I'm interested in whether or not my code operates properly at runtime.

Collapse
 
synthetic_rain profile image
Joshua Newell Diehl

One can use a recursive function similar to this one to freeze an object along with any objects nested within.
Obviously there are performance concerns depending on object size.

function deepFreeze(nestedObject) {
  const propNames = Object.getOwnPropertyNames(nestedObject);
  // Traverse
  for (const name of propNames) {
    const value = nestedObject[name];
    // Check for reference-type value
    if (value && typeof value === "object") {
      // Recurse
      deepFreeze(value);
    }
  }
  // Freeze each of type "object"
  return Object.freeze(nestedObject);
}
Enter fullscreen mode Exit fullscreen mode

And in your CONSTANTS module:

export const CONSTANTS = deepFreeze({
  SALES_TAX: {
    ALABAMA = 0.04;
    FLORIDA = 0.06;
    LOUISIANA = 0.0445;
  },
});
Enter fullscreen mode Exit fullscreen mode
Collapse
 
bytebodger profile image
Adam Nathaniel Davis

Yes. And to further your point, I have in fact created the exact same function in some of my apps before!

Collapse
 
synthetic_rain profile image
Joshua Newell Diehl

Thanks for your reply, Adam!
Keep writing.

Collapse
 
clamstew profile image
Clay Stewart

Maybe use the deepfreeze npm package instead of nested Object.freeze. Solid technique. I’ve used it myself in prod for years. Have to agree with the file vs nested convention though. Nesting will get gross

Collapse
 
mcsee profile image
Maxi Contieri • Edited

Great article!

consts are great but introduce coupling.
For example you cannot replace it them in tests
That's why, IMHO, we should use objects and functions with Dependency injection.
In your brilliant article, Frozen Objects and you can "replace them" in tests

 
bytebodger profile image
Adam Nathaniel Davis

OK. You win...

 
mindplay profile image
Rasmus Schultz

Group things into modules - that's what modules are for. Don't create arbitrary structures to work around an imagined problem or lacking IDE support.

Import modules with import * if you have to iterate over the keys - if you need a specific tax rate only, import that; don't import symbols you don't use. (So a person can make sense of dependencies, and so tree-shaking works better.)

I wonder what IDE you're using? In either WebStorm or VS Code, if I type SALES_ and hit CTRL+SPACE, it will even automatically add the import statement.

So I honestly don't even know the issue you're trying to work around.

The key point for me here is, don't add arbitrary structure to accommodate an IDE. Create the data structures you need. Use modules to group related symbols, the way they were intended. Any modern IDE should be more than able to keep up with that.

In fact, simpler patterns are usually better for IDE support. I mean, someone now has to know to find and import a symbol with an arbitrary name like CONSTANTS, which does not relate to anything in your business domain - whereas a name including the words SALES or TAX are immediately and easily found by an IDE, and easily recognized and confirmed by the user when they see a module name that matches in auto-complete.

In my opinion, you were doing it right to start with. 🙂

 
bytebodger profile image
Adam Nathaniel Davis

It solves a problem you shouldn't have - if you're already using TS or JSDocs. For reasons that I've explained in lonnnnng detail in previous posts, I'm not using TS. I don't have any "problem" with JSDocs (in fact, I've recommended it here dev.to/bytebodger/a-jsdoc-in-types...), but I don't often use that either.

Collapse
 
jackbrownandhiskeyboard profile image
Jack Brown

Great article mate!