DEV Community

Cover image for Why Do JS Devs Hate Namespaces?

Why Do JS Devs Hate Namespaces?

Adam Nathaniel Davis on December 06, 2020

I'm a huge believer in self-documenting code. In fact, I've already written about the idea that comments are a code smell - because I should just ...
Collapse
 
lionelrowe profile image
lionel-rowe

To provide a contrary perspective, why does allow need to be a class? Classes can be useful in situations where they might need to be instantiated with different parameters, but your example makes it looks like all the methods being consumed are static, and the class isn't being instantiated at all.

Surely this is the perfect use case for a module, and the principle of least power suggests that we shouldn't reach for a class when a module will do.

If the only issue is namespacing, that can easily be solved by using the import * as allow from '<path>' syntax, and it also provides the flexibility to only import certain functions if the consumer of the module wishes to do so.

Collapse
 
bytebodger profile image
Adam Nathaniel Davis

It was created as a class to facilitate chaining by constantly returning this. That being said, it's not the only way to accomplish that goal.

I don't personally disagree with your point - but I don't necessarily see it as a "contrary perspective" either. IMHO, it's more of an alternate perspective. Maybe that's splitting hairs. But the point is that I can't really see how a class, in this scenario, is wrong, but I can absolutely accept that maybe it's not preferred.

Consequently, I literally just started exploring making this an NPM package. (Like... last night.) And I'm thinking that it doesn't really need to be a class - but maybe a plain-ol' object.

Collapse
 
lionelrowe profile image
lionel-rowe • Edited

Hmm, a fluent interface does lend itself to OO and OO lends itself to classes, but could be implemented with a plain object too. Not saying this would necessarily be perfect for your use case, but it could be done.

const fluent = {
    fn1: () => { console.log(1); return fluent },
    fn2: () => { console.log(2); return fluent },
}

fluent.fn1().fn2()
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
bytebodger profile image
Adam Nathaniel Davis

I'm actually implementing it now with a module design pattern. So basically, it's a function... that looks a heckuva lot like a class.

github.com/bytebodger/allow/blob/m...

Collapse
 
michi profile image
Michael Z • Edited

I think most JS devs would define destructing as a "net good". And I'm certainly in that crowd. But some JS devs have come to embrace destructuring to the point that they believe ALL THE THINGS!!! should be destructured.

Wait, are you saying that the following is not clean and pure and beautiful? 😂😂

const { city: { id: cityId } } = { ...user, somethingExtra: '...' }
Enter fullscreen mode Exit fullscreen mode

Never understood why some prefer the above to a classic user.city.id.

Good post!

Collapse
 
bytebodger profile image
Adam Nathaniel Davis

Bingo! I didn't even get into all of the hoops that people sometimes jump through to destructure one choice little bit out of an object, but this is a perfect example.

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

I always look at JS from the perspective of a Lua developer, since that's my main language and they are both very similar in many aspects.

The lack of namespacing has always seemed a bit weird to me. The Lua equivalent to JS objects, tables, is used for namespacing almost everywhere, with most library code looking like this

local lib = {}

function lib.foo()
end

function lib.bar()
end

return lib
Enter fullscreen mode Exit fullscreen mode

yet in javascript nobody does this, despite it being possible in just the same way (except for the syntax, of course).

const lib = {}
lib.foo = (a) => a+1
lib foo = (a) => a*2
Enter fullscreen mode Exit fullscreen mode

For small sections of code, what JS was originally built for, this may be enough, but for larger codebases this just seems like just a valuable tool that I wonder just how the whole JS community never started adopting this. Then again, maybe it's just a leftover from those times when JS really was just used for simple interactivity on plain hand-written HTML pages.

Collapse
 
bytebodger profile image
Adam Nathaniel Davis

yet in javascript nobody does this, despite it being possible in just the same way (except for the syntax, of course).

I appreciate that confirmation! As I was writing the article, I couldn't help but wonder whether this lack of namespacing was just in my head. But yeah - I don't understand why the approach you've shown above is almost never taken in JS.

In fact, as I outlined in the section about destructuring, it honestly feels to me like JS devs are ruthlessly going in the other direction - purposely stripping variables of all context. I'm not exaggerating when I say that I've read JS code where I had to repeatedly refer back to the top of the function to understand the values that particular variables were supposed to hold - because all of those variables had been destructured out of their original object.

Collapse
 
shinigami92 profile image
Shinigami

Tree shaking

Collapse
 
sirseanofloxley profile image
Sean Allin Newell

His approach can still be tree shaken.

Collapse
 
bytebodger profile image
Adam Nathaniel Davis

I don't follow...

Collapse
 
shinigami92 profile image
Shinigami

Sorry for not directly writing back, was busy these days 🙂
So tree shaking can be performed by e.g. webpack and this is a strategy to minimize your bundled code
If you only import something like import { myFunctionA } from 'moduleX';, only myFunctionA will be bundled in the output code but e.g. myFunctionB would not.
If you now have my.functionA and my.functionB and you use import { my } from 'moduleX'; you are forced to bundle both functions into output code, also if you only need functionA
This hugely increases your bundled output, cause you are forced to import everything from allow in your example.

I may be wrong and @SeanAllinNewell may have another idea how you can still benefit from tree shaking. But currently that is what I understand under the term tree shaking and plugins can show the imported kb of an import statement in e.g. VSCode.

Thread Thread
 
bytebodger profile image
Adam Nathaniel Davis

OK. I know what tree-shaking is. But I didn't understand what exactly you were getting at. A few thoughts on this:

  1. I understand the need to import components separately. But I think it can sometimes get kinda silly when you're talking about functions. For example, my allow library is used throughout my entire app. Most of the functions in that library will be used somewhere in the app. So it's needlessly specific to force the coder to import each one of those functions independently.

  2. The allow library has 169 LoC that encompass 19 functions. The raw file is 6.59 KB. I haven't even tried to look up how small this becomes once it's minified. But the point is that it will be tiny.

But you bring up a good point about bundle size. Specifically, I've found that, too often, JS devs make choices that undermine the readability (and thus, the maintainability) of their own code in the name of bundle size. So... a few more thoughts on that thought:

  1. If you have absolute control over your app and how it's rendered, then I can kinda understand the desire to minimize bundle size. But this is rarely the case in corporate apps. Typically, once your app is deployed, it's put in a wrapper that has a ton of ads, trackers, analytics, images, video, and other such detritus. For example, if you go to espn.com with no ad blockers, their homepage currently uses up 6.5 mega bytes. And this is not unique in corporate environments. I just can't get too worked up about 5KB here-or-there in an app's bundle when the sites we frequent nowadays are often multiple megabytes.

  2. IMHO, tweaking bundle size is usually a micro-optimization. I understand that there are some situations with some apps where bundle size can be critical. I also understand that, for most apps, the functional difference between a 100k bundle and a 105k bundle is... nothing. And even if you want to take the stance that all those things add up, well then... so what? Because, again, the functional difference between a 100k bundle and a 300k bundle is typically... nothing.

  3. Although this article may feel a bit esoteric to most, I wrote it because this is another of those little issues that gnaws at me because it affects the readability of code. And readability isn't a "nice to have". More readable code is more maintainable code. If devs are consciously sacrificing readability for bundle size, then for most apps in most environments, they're making the wrong choice.

Thread Thread
 
shinigami92 profile image
Shinigami

Totally understand your POV
Just wanted to say this so newbies that read your article don't follow it blindly without thinking about the consequences 🙂

Thread Thread
 
bytebodger profile image
Adam Nathaniel Davis

Good point!

Collapse
 
ivan_jrmc profile image
Ivan Jeremic

You love classes!

Collapse
 
bytebodger profile image
Adam Nathaniel Davis • Edited

<EvilLaughter>Muahahaha!</EvilLaughter>

Collapse
 
ivan_jrmc profile image
Ivan Jeremic

useMuhHaha()

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
bytebodger profile image
Adam Nathaniel Davis

Huh???

Collapse
 
merri profile image
Vesa Piittinen

At this point one could argue your comment section tends to get full of unwanted side effects. But at least you can defend your opinion by saying the naysayers have no class.

Thread Thread
 
bytebodger profile image
Adam Nathaniel Davis • Edited

This is the optimal proportion of cheese, clever, and dad joke. 😂

Collapse
 
sirseanofloxley profile image
Sean Allin Newell

There are no guarantees of effectual code regardless of destructuring. Importing anything can have unwanted side effects!!