JavaScript, I love you, you’re perfect, now change

Avatar of Sarah Drasner
Sarah Drasner on

Those of us who celebrate Christmas or Hannukkah probably have strong memories of the excitement of December. Do you remember the months leading up to Christmas, when your imagination exploded with ideas, answers to the big question “What do you want for Christmas?” As a kid, because you aren’t bogged down by adult responsibility and even the bounds of reality, the list could range anywhere from “legos” to “a trip to the moon” (which is seeming like will be more likely in years to come).

Thinking outside of an accepted base premise—the confines of what we know something to be—can be a useful mental exercise. I love JavaScript, for instance, but what if, like Christmas as a kid, I could just decide what it could be? There are small tweaks to the syntax that would not change my life, but make it just that much better. Let’s take a look.

As my coworker and friend Brian Holt says,

Get out your paintbrushes! Today, we’re bikeshedding!

Template Literals

First off, I should say, template literals were quite possibly my favorite thing about ES6. As someone who regularly manipulates SVG path strings, moving from string concatenation to template literals quite literally changed my damn life. Check out the return of this function:

function newWobble(rate, startX) {
  ...
  
  if (i % 2 === 0) {
    pathArr2[i] = pathArr2[i] + " Q " + in1 + " " + QRate;
  } else {
    pathArr2[i] = pathArr2[i] + " Q " + in2 + " " + QRate;
  }

  ...
  return "M" + pathArr2.join("") + " " + startX + " " + (inc * (rate*2) + rate);
}

Instead becomes

const newWobble = (rate, startX) => {
  ...
  
  if (i % 2 === 0) {
    pathArr2[i] = `${pathArr2[i]} Q ${in1} ${QRate}`;
  } else {
    pathArr2[i] = `${pathArr2[i]} Q ${in2} ${QRate}`;
  }

  ...
  return `M${pathArr2.join("")} ${startX} ${(inc * (rate*2) + rate)}`;
}

…which is much easier to read and work with. But could this be improved? Of course it can!

There is a small bit of cognitive load incurred when we have to parse ${x}, mostly due to the very nature of the characters themselves. So, what if template literals lost the dollar sign and moved to square brackets instead? Rather than:

return `M${pathArr2.join("")} ${startX} ${(inc * (rate*2) + rate)}`

…we can have something like:

return `M[pathArr2.join("")] [startX] [(inc * (rate*2) + rate)]`

…which is much more streamlined.

Ternary operators

Ternary operators are interesting because in recent years, they have not changed, but we did. A lot of modern JavaScript makes heavy use of ternaries, which causes me to revisit their syntax as it stands now.

For instance, a one-liner like:

const func = function( .. ) {
  return condition1 ? value1 : value2
}

…is not so hard to read and grok. But here’s what I’ve been reading a lot lately:

const func = function( .. ) {
  return condition1 ? value1
       : condition2 ? value2
       : condition3 ? value3
       :              value4
}

This is much harder to read, mostly because the colon : gets lost depending on your code editor and syntax highlighting settings. And, what if someone isn’t properly formatting that code? It can easily become:

const func = function( .. ) {
  return condition1 ? value1 : condition2 ? value2 : condition3 ? value3 : value4
}

…in which case the colons are extremely hard to see at a glance. So what if we used a visual indicator that was a little stronger?

const func = function( .. ) {
  return condition1 ? value1 | condition2 ? value2 | condition3 ? value3 | value4
}

A pipe doesn’t break up the flow, yet still separates in a way that is not as easy to get lost in the line.

Arrow Functions

I’m going to have a mob after me for this one because it’s everyone’s favorite, but arrow functions were always a miss for me. Not because they aren’t useful—quite the opposite. Arrow functions are wonderful! But there was always something about the legibility of that fat arrow that irked me. I am used to them now, but it troubled me that when I was first learning them, it took me an extra second or two to read them. Eventually this passed, but let’s pretend we can have our cake and eat it too.

I am definitely not suggesting that we still use the word function. In fact, I would love it if arrow functions weren’t anonymous by nature because:

const foo = (y) => { 
  const x
  return x + y
}

…is not quite as elegant as:

const foo(y) => {
  const x
  return x + y
}

In my perfect world, we would drop the function and the arrow so that we could have something that resembles more of a method:

foo(y) {
  const x
  return x + y
}

and an anonymous function could simply be:

(y) {
  const x
  return x + y
}

Or even a one liner:

(y) { y += 1 }

I know many people will bring up the fact that:

  1. arrow functions have one-liners that do this, and
  2. I disliked the curly brackets in the template literals above

The reason I like this is that:

  1. some encapsulation can provide clarity, especially for logic, and
  2. curly brackets are a stronger visual signal, because they’re more visual noise. Functions are important enough to need that sort of high-level visual status, whereas template literals are not.

OK, now let’s go one step deeper. What if we always had an implicit return on the last line? So, now we could do:

foo(y) {
  const x
  x + y
}

Or…

(y) {
  const x
  x + y
}

If we didn’t want to return, we could still say:

foo(y) {
  const x
  x + y
  return
}

Or, better yet, use a special character:

foo(y) {
  const x
  x + y
  ^
}

This way, anytime you wanted to return a different line instead of the last, you could use return and it would work just as normal:

foo(y) {
  const x
  return x + y
  const z
}

What a world it could be, eh?

What Now?

People invent new languages and rewrite compilers for the very reason of having a strong opinion on how a language should pivot or even how it should be written at all. Some of my favorite examples of this include whitespace, which is a programming language created from all tabs and spaces, and Malbolge, which was specifically designed to be impossible to program with. (If you think I’m a troll for writing this article, I got nuthin’ on the guy who wrote Malbolge.) From the article:

Indeed, the author himself has never written a single Malbolge program

For those more serious about wanting to develop their own programming language, there are resources available to you, and it’s pretty interesting to learn.

I realize that there are reasons JavaScript can’t make these changes. This article is not intended to be a TC39 proposal, it’s merely a thought exercise. It’s fun to reimagine the things you see as immovable to check your own assumptions about base premises. Necessity might be the mother of invention, but play is its father.

Many thanks to Brian Holt and Kent C. Dodds for indulging me and proofing this article.