DEV Community

Dave Jacoby
Dave Jacoby

Posted on • Originally published at jacoby.github.io on

Namespaces and Javascript: I Don’t Know What I’m Doing

Here’s a simple JS library I wrote today:

let rr = {};
rr.data = {};
rr.code = {};

rr.code.listen = function (event){
  console.log(this.responseText);
  location.reload();
}

rr.code.remove_request = function () {
  let id_span = document.getElementById("run_id");
  let run_id = id_span.getAttribute("data-run-id");
  console.log([run_id, Date.now()].join("-"))
  let request_id = prompt("Enter the request in Run #" + run_id + " that you want removed");
  if (!(request_id == null || request_id == "")) {
    let url = ["/cgi-bin/dev/dave/env.cgi", run_id, request_id].join("/");
    console.log(url);
    let xhr = new XMLHttpRequest();
    xhr.addEventListener("load",rr.code.listen);
    xhr.open("POST",url);
    xhr.send();
  }
}

// This is part of the *LIMIT TO TEST* aspect of this task.
// I will likely just make this set the onclick
rr.code.start = function () {
  let junk_drawer = document.getElementById("junk_drawer");
  let button = document.createElement('span');
  button.innerText = "Remove Request";
  button.id = "remove_request";
  button.onclick = rr.code.remove_request;
  button.classList.add("button");
  junk_drawer.appendChild(button);
}

window.onload = rr.code.start;

There are a few “conventional” choices – choices made for convention. I put window.onload at the bottom, for example, because in some languages, you get problems if you use a function before you define it. In my language of choice, that matters not, but I am not 100% sure if it matters in JS.

In other places, I have used Bootstrap to make popups that only give the users actual, actionable choices, rather than having them fill a prompt and parsing it, but I figure I will have to parse it on the client end anyway, and this is the quick-and-easy UI choice.

I use let instead of var everywhere and all the time. I think that first let rr puts rr in the global scope, which doesn’t particularly limit anything, but using it everywhere removes the doubt of “should I use it here?”

And then there’s let rr = {}. Could as easily have been let rr = new Object, but that ‘s not the real point. I could have made all the functions function sub () {...} instead of rr.code.sub = function () {...}, but then we may hit issues.

We hit those issues because your libraries are not all that can be running on your page. I just created a web page that was just this –

<!DOCTYPE html>
<html>
</html>

– and Chrome Dev Tools show that content.js and antiphishing.js are running. This and whatever libraries you or your coworkers write or include, and you think you’re the only one who would ever want to write start()?

However, I’m seeing things like document.getElementsByTagNameNS() and suspect there’s a new, cool, somehow more appropriate but unfathomable to me. I cover all sorts of things, and I can have an all-tech staff meeting on my commute in the morning, so if any toes get stepped on with regard to namespaces, it’ll be my shoes doing the stepping.

So, I’m curious: What is the best way to handle JS namespaces? How do you ensure that you don’t break your users toys when throwing more code into a page?

If you have any questions or comments, I would be glad to hear it. Ask me on Twitter or make an issue on my blog repo.

Top comments (6)

Collapse
 
joelnet profile image
JavaScript Joel • Edited

So, I’m curious: What is the best way to handle JS namespaces?

I don't namespace.

I use es modules and bundle them with webpack (for web apps). This way I never pollute the global namespace. This is the approach I would recommend.

In the past (previous to webpack) I have also use an IIFE to encapsulate my code to prevent it from being exposed globally.

(function(global) {
  // code goes here
}(window || global))

However... I have had some times where I am forced to make something global. This was in a legacy application that was a mess.

I used a function similar to this:

const namespace = (context, name = '', object) => {
  const [head, ...tail] = name.split('.')
  if (tail.length > 0) {
    return namespace(context[head] = context[head] || {}, tail.join('.'), object)
  }
  context[head] = object
}

namespace(window, 'math.add', (x, y) => {
  return x + y
})

window.math.add(3, 4) //=> 7

p.s. I would run through a little testing with that function, I just kinda whipped it up on the spot, so I don't know if it's buggy.

Cheers!

Collapse
 
jacoby profile image
Dave Jacoby

I'd be happy to learn more about this approach. I'm very trailing-edge in most of the technologies I use, because I regularly switch between dev to admin to helpdesk, so a link that goes into how this works and why would be appreciated.

Thanks!

Collapse
 
joelnet profile image
JavaScript Joel

If you are just starting out with this, I would recommend looking into Parcel (Getting Started). It's an easy to configure bundler. You can get up and running in a few minutes.

I personally prefer Webpack (Getting started) because I am more familiar with it. You'll have to be familiar with other toolsets, like babel or webpack-devserver, etc. It's more work.

Got questions, hit me up on Twitter!

Cheers!

Collapse
 
anduser96 profile image
Andrei Gatej

What I would do is to have a file from which I export constant variables.
For example:
const addPost = ‘post/add’
const addComment = ‘comment/add’

At least one downside of using this approach would be that you have to include this file everywhere you are going to use that specific function for that specific namespace.

I have tried this when developing a Vue app with Vuex, but fortunately, Vuex has also the concept of namespaced modules(I know I’m going a little bit beyond the scope of the article).

Here is a demo of both implementations: github.com/Andrei0872/vue-pocket-r...

Hope you found this useful!

Collapse
 
jacoby profile image
Dave Jacoby

If you'll excuse me, I'm not really seeing what that buys me. I guess I knew that const was added to ES at the same time as let. Can you do const addPost = function () { /* adding post here */ }?

My prime concern is keeping my "toys" to the corner of the "playground" so nobody else's stuff gets mixed up in mine, and vice versa. It isn't immediately clear what this does to help me.

(Disclaimer: JS Dev is one of the hats I wear only rarely. I've looked at Vue.JS once, maybe. People tell me it's nice.)

Collapse
 
anduser96 profile image
Andrei Gatej

I’m sorry, now that I look twice, my answer is pretty misleading.

But just to finish my idea:
const addPost = ‘post/add’
You could define this as an obj prop:
{
addPost { ...}
}

Anyway, it doesn’t seem to be practical. There are definitely better ways that have already been mentioned.