by

Lessons from a Tridion Retreat: JavaScript for Java guys

Reading Time: 9 minutes

One of the really cool things to come out of this year’s retreat was a lot of discussion around JavaScript. About 90% of the programming I do is in JavaScript. Not only is it the language where I have the most experience, it’s the language I like the most. It’s quirky and weird at times, I know. But, in light of some recent work on DXA-JS (which, I know, merits its own blog post), I’d like to expand on a few of the JavaScript questions and topics that came up at the retreat. Those answers are relevant for any Tridion person that may be touching JavaScript in the future, but also any back-end developer that isn’t generally familiar with what changes have been going on in the JavaScript language.

JSON is JavaScript

JSON is an acronym for JavaScript Object Notation. It’s a standard created by Douglas Crockford. He figured that the best way to get folks to adopt the standard was to throw up his own specification on a website, and it worked. Now we have JSON, which is a subset of JavaScript 1

The JSON is a global object provided by the environment. So if it’s a browser, you’ll find it under window.JSON, and if it’s node.js, you’ll find it as JSON. So use JSON.parse() and JSON.stringify() to your heart’s content.

This fact is why DD4T 2.0 is so uniquely suited for modern web applications. JavaScript frameworks are a dime-a-dozen these days, but as long as they can get their data in valid JSON, then a fully rendered view is just a JSON.parse() away!

Strings and templates are easier than ever

ES6 (ECMAScript 6 / the 2015 changes to JavaScript) introduced a load of new features to the language. One of the most powerful features we got is called “template literals”. Template Literals are just an improved way to write strings that make it a piece of cake to write Views / Templates in JavaScript now.

Let’s say you wanted to make a GUI extension that duplicated a field. In the olden days of yor, you might have written this:

$('.some_field').parents('.field-container').append('<div class="other_field"><label>' + someLabel +'</label><input id="' + someId + '" type="text" value="' + someValue+'" /></div>");

Tell me what’s worse: looking at that, or writing it? God help you if you want to write that across multiple lines, too.

Template Literals introduce a much cleaner and more sane way to write strings. A template literal is denoted by the backtick ( ` ), and it allows you to interpolate expressions with ${}. So here’s our jQuery expression written as a template literal:

$('.some_field').parents('.field-container').append(`
  <div class="other_field">
    <label>${someLabel}</label>
    <input id="${someId}" type="text" value="${someValue}" />
  </div>
`);

 

Oh man, doesn’t that look nice? By the way, template literals allow multiple lines.

When I worked on DXA-JS over the MVP retreat, template literals were the hero of the day in writing my views

There’s some new ways to declare variables

ES6 introduced a new way to deal with variables. In particular, it introduced let and const.

Constants

const is exactly what you think it is, it’s a constant. Keep in mind, though, it doesn’t mean you can’t change the value of the variable—it means you can’t reassign it. I’ll demonstrate:

This is bad, and predictably so:

const foo = "foo";
 
foo = "bar"; // Uncaught TypeError: Assignment to constant variable;

This is fine, and confusingly so:

const foo = [];
const baz = {};

foo.push('bar');
baz.babble = "foo";

Again, the const keyword means that you can’t reassign. It doesn’t mean the value is fixed.

Ok, so what is let for ?

const makes sense. After all, it’s a way to (sort of) make constants in JavaScript. What the heck is let supposed to be used for?

First, let’s talk about something really weird about JavaScript. Let’s make a button and add an event listener:

for (var i = 0; i < 5; i++) {
  var btn = document.createElement('button');

  btn.appendChild(document.createTextNode(`Button ${i}`));

  btn.addEventListener('click', function(){ 
    console.log(i); 
  });

  document.body.appendChild(btn);
}

When the user clicks on Button 4, what do you think will be logged into the console?

When the user clicks on Button 0, what will get logged in the console?

Doesn’t matter which button, i is going to be 5.

This is because, in JavaScript, variables are function-scoped. If you’ve run this loop in the console, or a nodejs CLI, and it’s not wrapped in any sort of “function” to speak of, i becomes a member of global scope. And when any of those buttons get clicked, they’re accessing the last value set on the variable, which is 5. Function-scoped variables are all sorts of headaches.

In the past, we’ve gotten around this by using a function to create a scope:

for (var i = 0; i < 5; i++) {
  var btn = document.createElement('button');

  btn.appendChild(document.createTextNode(`Button ${i}`));
   // IIFE: Immediately invoked function expression, for the purpose of creating a scope so i is correct
  (function (i) {
    btn.addEventListener('click', function() { 
    console.log(i); 
    });
  })(i);

  document.body.appendChild(btn);
}

Now, we don’t have to do that any more. Because let is a block-scoped variable.

for (let i = 0; i < 5; i++) {
  var btn = document.createElement('button');

  btn.appendChild(document.createTextNode(`Button ${i}`));

  btn.addEventListener('click', function(){ 
    console.log(i); 
  });

  document.body.appendChild(btn);
}

By setting i with let, we’ve assured that the value of the variable doesn’t “leak” outside of the the block scope.

Oh, By the way, const is block-scoped, too. Now, there’s really no use for var any more, unless you actually want a function scoped variable;

classes are a thing now

If you start working with React, Preact, Angular, or Vue frameworks, you’ll get about 1 minute into the first tutorial before you encounter the class keyword. ES6 introduced classes, much to my dismay2.

So, you really can write something like this in React:

function Button(props) {
  return (
    <button>
      {props.value}
    </button>
  );
}

class CTA extends React.Component {
  renderCTA(i) {
    return (
      <button value="{this.props.buttons[i]}"> this.props.onClick(i)}</button>
    );
  }

I personally think that’s a stupid unusual usage of the paradigm2, but who am I to judge? You now have class, extends, and super available to you.

Functions are easier to write

One of my absolute favoritest features of ES6 is the arrow function, which just makes JavaScript easier to write and understand. Let’s go back to a Tridion GUI extension. Suppose we want to encapsulate our code into a module, and add an event listener that executes a function. It would look something like this:

var module = {
    init: function () {
        var _this = this;

        $('.some_field').on('click', function () {
            _this.doThing()
        });
    },
    doThing: function () {
        // do things
    }
}

Take note of var _this = this. We’re doing this because this is determined by the function 3

With an arrow function,there’s no need to save this as a variable.

 init: function () {
        $('.some_field').on('click',  () => {
            _this.doThing()
        });
    },
    doThing: function () {
        // do things
    }
}

 

The arrow function is a scopeless function. That means this is inherited from the scope around it, which makes it super handy for just about anything that requires a callback. The added bonus is that it’s easier to write. GUI extensions are easier to write, and even client-side code is more sensible when you’re not constantly struggling with tracking down your this.

Type Coercion and Comparisons

This is by far my favorite feature of the language. It’s also the weirdest part, too. Type coercion happens everywhere, all the time, including your comparison operators. Before proceeding further, grab your favor liquor, and a shot glass.

Every time something doesn’t make sense (I’ll decide when that is), take a shot.

Also, go ahead and email your boss and tell them you aren’t going to make it in tomorrow.

Fact:

It is incorrect to say that a == b compares only value, while a === b compares type and value.

A correct way to explain == is that it means, “allow type coercion”, while === means, “do not allow type coercion”.

Take a shot

Let’s start with some variables:

var stringFalse = "false";
var boolFalse = false;
var stringEmpty = "";
var stringZero = "0"
var intZero = 0;
var undef;

 

If == compared only value, then stringFalse == boolFalse would return true. But it doesn’t. It returns false. Run it in the console if you don’t believe me.

Because the == means, “allow type coercion”, what happened is the JavaScript engine coerced boolFalse into a number.

  1. So, stringFalse == boolFalse became stringFalse == 0 .
  2. Then on the left hand side, JavaScript coerced the string to a boolean, so ‘false’ == 0 became true == 0 .; only on the principle that strings with values are true.
  3. On the left side again,true coerces to 1
  4. So the actual comparison operation ends up being 1 == 0

You should still be cleared to drive. Now let’s change that.

So, what the hell does “allow type coercion” actually mean?

It means that you are giving the JavaScript engine permission to fiddle around with different ways to represent these values, until it can find some sort of match. == effectively means, “see if you can find any possible way to make this work”.

For this reason intZero == boolFalse returns true. The confusion in “allow type coercion” comes from not knowing the steps taken to get there.

Let’s break down intZero == boolFalse:

  1. Right side: boolFalse coerces into a number (0)
  2. left side:intZero is already a number, no more coercion necessary
  3. Final Comparison: 0 === 0
  4. Result: true

All of these end up being true:

  • stringEmpty == intZero
  • stringEmpty == boolFalse
  • stringZero == intZero
  • stringZero == boolFalse

And they do so because of the coercion rules:

  • 0 coerces to false
  • “” coerces to 0
  • ‘0’ coerces to 0
  • false coerces to 0

It’s got nothing to do with “type”, really. It’s got to do with which types can become which other types.

Don’t ever be deceived by anyone who tells you that === compares type and value, while == doesn’t. That’s just !true.

Now, let’s get you drunk…

It’s the Rules for Coercion Rules that are Hard

Type coercion is good. What’s hard is figuring out the rules, like how undef == boolFalse is still false. If that makes sense to you, take a shot

Also, since everything coerces, that means [] == 0 is true.

You might think to yourself, “an empty array is kinda like an empty string. Empty things coerce to zero or false. Makes sense”

Wrong. Take a shot.

So, an empty array coerces down to zero. This is why ['foo'] == 0 is false.

Doesn’t that mean that ['foo'] == true is true? Wrong.

Take a shot.

Let’s flip this around, and talk about why ['foo'] == 'foo' is true. It’s because arrays coerce to strings.

So, when you write ['foo'] == true, that’s actually like writing [‘foo’] == ‘true’. And JavaScript has this cute feature where it coerces arrays to strings. So that means you really wrote ‘foo’ == ‘true’ , and that’s absolutely, positively false.

So why did [] == 0 end up true? Because it coerced down to “” == 0 . Empty strings coerces to false. And so does 0. so you ended up with false == false which is true

This same type coercion weirdness is also why ['foo','bar'] == 'foo' is false.

If that’s a surprise, take a shot.

['foo','bar'] coerces down to ‘foo,bar’. So you really wrote ‘foo,bar’ == ‘foo’; it makes sense why you got false, now, doesn’t it?

Let’s go over some new type coercion rules:

  • [] coerces to string
  • {} coerces to string
  • null coerces to undefined
  • undefined coerces to null

Oh, and a quick reminder: all of the rules about == are consistent with all of the logical operators; this all applies to && and || as well.

So if you thought this was just about to == and ===, take a shot.

Safe Type Coercion

Despite all the scary things we’ve talked about, type coercion is actually fine. There’s just two things you want to look out for in your operators:

  1. If either side has true or false, don’t use ==
  2. If either side has [], “”, or 0, seriously consider avoiding ==

And guess what we’ve been hammering on this whole time? The Edge Cases.

JavaScript is a language where “Truthy” and “Falsy” aren’t cute words Stephen Colbert made up; “truthy” and “falsy” are legitimate terms we use to address implicit type coercion. Outside of the edge cases, it ends up being a fairly predictable situation that can make our lives substantially easier.

Imagine a function where you only want something to happen if a parameter is sent in. If you wrote it, without appreciating type coercion, it might look like this:

function doThings(something) {
  if (something !== undefined || something !== '' || something !== null ) {
    //do things
  }
}

 

So long as something isn’t a number, you can safely write:

function doThings(something) {
  if (something) {
    //do things
  }
}

 

Isn’t that nice? We let type coercion figure this stuff out!

And if something could be a number, then there’s a simple enough way to modify it by using isNaN(), which is a global that checks to see if a value is a number.

function doThings(something) {
  if (something || !isNaN(something)) {
    // do things
  }
}

 

This, again, is useful knowledge for writing GUI extensions where inputs may be involved. You don’t have to do as many kinds of equality checks as you might think.

Type coercion is a bit weird, but, the rules are actually very well known and documented4. You don’t have to live and die by === for the rest of your life.

Wrapping it up

I really enjoyed the MVP retreat because it gave me a chance to flex my JavaScript muscles and to show off what JavaScript can do. it was fun to have an impromptu JavaScript session at 11:00pm with beers in our hands. It was also fun to talk about the cool new templating literals, and JSON.Parse(), and even rag on classes. I love these kinds of sessions.

My hope is that we can have another one about Powershell some time in the near future, too. I guess that’s not as impromptu, though…

1 Because pedantry and programming are synonymous, I should point out that JSON is not technically JavaScript, or a subset. JSON allows certain characters that JavaScript does not, like invisible spaces. The kinds of people who make this argument are the kinds of people that make PowerPoint turing complete.

2 That’s not to say that I haven’t used ES6 classes, I have. But it’s just syntactic sugar sitting on top of the Prototype pattern. It was put there to appease all the folks that were complaining that JS wasn’t Object-Oriented. Which is ridiculous because functions are objects and the middle word of DOM is Object. Traditional languages produce a copy from parent to child. JavaScript doesn’t do that—even now with your beautiful class keyword, because it’s still just a delegation link. You don’t need class for anything. But Facebook uses it, so it must be good! The people who have a website running on Fake PHP are using classes for views and everyone thinks this is brilliant.

3Kind of. this is actually determined at the call site of the function. But, try not to think too hard about it right now, I probably got you really drunk.

4Suuuper well documented. Kyle Simpson’s Series of Books, “You don’t Know JS”, goes into excruciating detail on the subject of type coercion. If you’re (still) interested in learning the language, I’d recommend these books.