The busy JavaScript developer's guide to ECMAScript 6, Part 1

Variable declarations and more in the new JavaScript

Syntactic sugar strengthens core features in ECMAScript 6

Comments

Content series:

This content is part # of 4 in the series: The busy JavaScript developer's guide to ECMAScript 6, Part 1

Stay tuned for additional content in this series.

This content is part of the series:The busy JavaScript developer's guide to ECMAScript 6, Part 1

Stay tuned for additional content in this series.

ECMAScript (more commonly known as JavaScript) is a scripting language with a checkered history. It was originally assembled as an extensibility point for Netscape Navigator, one of the first web browsers. Originally named for the connection to Java, which was rapidly developing a cool factor at the time, it was panned for many years as the lesser of the two languages.

Morphing to the ECMA standard in 1997 won the language some credibility, but the first major standard release, ECMAScript 4, split the community in half. It took a timorous, errata-style release—ECMAScript 5, so-called "Harmony"—to rebuild any sense of unity.

Lately, though, JavaScript has taken the role of the comeback kid. Buoyed by Node.js, it has emerged as a server-side powerhouse. After being formally adopted as a standard in June 2015, ECMAScript 2015 (also known as ECMAScript 6, or ES6) is coming into mainstream use. ECMAScript 2016 was adopted as a specification in June 2016.

With libraries and packages in the npm registry migrating up to the latest version, it's time that JavaScript developers learned what's new in the current standard. Without a doubt, once you see some of these features, you'll want to start using them in your code.

We'll get started here, with a look at six language changes that will make a big impact in the flexibility and efficiency of your code. Of them all, the new destructuring assignment operation is probably the most significant, so I'll run through a few scenarios for using it, both here and in Part 2.

Migration strategies

Although ECMAScript 6 introduces exciting changes to the language, most would be classified as syntactic sugar. In many cases the new standard introduces efficiences by changing the way that the code is written, not what it does. Those updates are mostly responsive to what developers were already doing—shortcuts and workarounds that have now been standardized. Ideally, bringing the new language features into play will make the same code easier and faster to write.

From a migration perspective, focusing on syntactic sugar has advantages. There's less of a cognitive load in extending what you know about a feature than there is in learning a new one. On the other hand, there are now multiple ways to achieve the same result: the new ECMAScript 6 way, and the old way. That can be confusing at times, at least until your migration reaches a tipping point. Because so many of the changes can be flexibly adopted, it's possible to pursue a more gradual and flexible migration strategy. For many, that will be the most reasonable path. ECMAScript brings a hefty set of new features, and trying to integrate them at once could be too much.

Another migration issue to consider is the compatibility of your environment. As we know too well, old browsers take forever to cycle out of use—just look at Internet Explorer as case in point. Fortunately, if your client side isn't 100% ECMAScript 6 compatible, you can use a "transpiler" to transform ECMAScript 6 source into ECMAScript 5-compatible code. For two of the more popular tools, see Traceur and BabelJS.

We can also be glad that the server environment is entirely under our control. As of this writing, the latest versions of Node.js implement 92% of the new standard. Unless you're looking at a feature in the remaining 8%, you should be able to run ECMAScript 6 in Node.js without additional setup.

That's enough overviewing for now; let's get our feet wet.

Syntax changes

Syntax changes are the most obvious and in some cases the easiest to understand. While ECMAScript 6 didn't change much about the overall JavaScript syntax—it's still very much a C-based language with semicolons and curly braces—it does clean up a number of "little" things that make a subtle difference to developers. We'll look at a handful of those here.

Unicode 8.0

First of all, ECMAScript 6 now requires conformance with Unicode Standard version 8.0 or later. Among other things, this means that an ECMAScript-compatible environment must accept smiley faces, since 8.0 has character code points for emojis.

Because most text editors don't yet directly support Unicode 8.0's full range of character code points, you'll implement most Unicode characters as escapes, such as \u{1d306} in a string literal. Because you can also use Unicode for identifiers, ECMAScript 6 supports Unicode escape syntax for variable names, as well. Here's an example:

    var \u{102C0} = { \u{102C0} : 2 };
    return \u{102C0};

Most of the time, developers will use Unicode for internationalization purposes, meaning it will appear inside a string literal, not as a variable identifier. Still, the new flexibility in variable identifiers will be welcomed by developers looking to use a language other than English in their code.

Binary and octal literals

Consider the ubiquitous integer literal: 9. Given that this is a literal value, and given the dominance of the Arabic numeral system (a base-10 decimal system), 9 is considered a decimal literal. Computers don't think in decimal terms, however; they think in binary, octal, or hexadecimal terms. ECMAScript has long allowed for hex literals, indicated by prefixing the literal with a 0x. In ECMAScript 6, you can express literals in binary (base-2) and octal (base-8) by prefixing the literal with 0b or 0o, respectively. Thus, in ECMAScript 6, all of the literals in the following expression print true, because they're all the same value:

    var decimalLit = 15;
    var hexadecimalLit = 0xF;
    var octalLit = 0o17;
    var binaryLit = 0b1111;
    console.log(decimalLit == hexadecimalLit);
    console.log(decimalLit == octalLit);
    console.log(decimalLit == binaryLit);

String templates and interpolation

For years, ECMAScript developers have been using awkward string concatenation to put variable members into strings, like so:

    var message = "The user " + user.firstName + " " + user.lastName + 
      " cannot be " + action + " because " + validationError;

While not the largest source of bugs in JavaScript, this strategy is error prone, not to mention hard to read. In terms of usability, it also puts JavaScript behind languages that support string interpolation. Rather than continue to miss the party, ECMAScript 6 introduces the backtick string literal. This literal lets you use the backtick character (`) to denote a string literal that supports string interpolation:

    var message = `The user ${user.firstName} ${user.lastName} cannot be
      ${action} because ${validationError}`;

The hard line-break between be and ${action} is because backtick strings are also "multiline literals." This means that whitespace is preserved within the string, so the message above would appear on two lines. It would break after "be," then display two spaces (because the code indents two spaces) before continuing.

Of course it's unfortunate that ECMAScript 6 had to introduce a new kind of string literal, instead of simply supporting interpolation on the existing single-quote or double-quote strings. The backward-compatibility implications of supporting interpolation would have been huge, however. This is probably the best solution available. Over time, we can expect most string literals to become backticked strings.

Variable declaration: let and const

ECMAScript has historically allowed programmers to use variables without declaring them. Doing so implicitly made them global, however, which was considered bad form. (Unless it was called a "Singleton," and then it was considered a pattern.) To work around this, JavaScript programmers started using the var declaration style to declare variables before use.

Unlike many other languages, ECMAScript never had an issue with a particular variable being redeclared multiple times. Thus, under old-school ECMAScript you could write the following without effect:

    var msg = "Howdy";
    var msg = "Hello there"; // acceptable, just reassigns

The second declaration was never an error. In the case above, the original variable was simply reassigned the new value. This was a subtle source of bugs, and quite the surprise to the C/C++/Java/C# developer. On top of that, ECMAScript never had a facility for creating immutable variables similar to final in Java or const in C# or C++.

ECMAScript 6 fixes all of that: First, it suggests replacing the use of var with let, which may not be redeclared. Beyond that, let operates much the same way that var did:

    var msg = "Howdy";
    var msg = "Hello there"; // acceptable, just reassigns
    
    let message = `This is your message.`;
    let message = `This is another message.`; // ERROR!

Using const in a variable declaration addresses the need for immutability. Once set, a const-declared variable may never be modified:

    const message = `This is your message.`;
    message = `This is your second message.`; // ERROR!

While a const-declared variable may not change its value, the object that the variable points to isn't constant, so it may still be modified:

    const user = request.user;
    user = new User(); // ERROR!
    user.authorized = true; // acceptable, changes a property

Going forward, my advice is to use const first. You can always change the declaration to let if it turns out the variable needs to be modified.

Block scoping

Speaking of variable declarations, it comes as a surprise to many ECMAScript developers that declared variables aren't bound to the "block" in which they are declared. Instead, they're bound to the function. Here's an example of function-scoped variables:

  function bar() {
    var i = 10;
    while (i > 0) {
      var j = 1;
      while (j > 0) {
        var i = 1; // This is the same i as above
        j--;
        i--;
      }
    }
  }

The above function will essentially rip through the nested loops exactly once, since the second "declaration" of i actually just assigns it the value of 1. Probably not the programmer's intent, all things considered.

As of ECMAScript 6, let- and const-declared variables are block-scoped, so they'll go out of scope when the current expression block ends, not when the function ends. Rewriting the previous code to use let instead of var yields the expected behavior:

  function bar() {
  let i = 10;
  while (i > 0) {
    let j = 1;
    while (j > 0) {
      let i = 1; // Different i
      j--;
      i--;
    }
  } // This loop will execute 10 times, as intended
}

In the bigger picture, block-scoping simply means that let- and const-declared variables will behave as variables do in every other C-based language. That's an improvement over the rather surprising way that ECMAScript has managed things up until now.

Destructuring assignment

There's one last change related to variable declaration, and it's one of the most significant updates in ECMAScript 6. A destructuring assignment lets you assign multiple variables out of a single array or object. In essence, this operation "destructs" the array or object into its constituent parts.

Destructuring is probably best understood in action, not words. Given an array like this one

    let names = ["Ted", "Jenni", "Athen"];

you could use the destructuring form of variable declaration to break out individual array elements into separate variables, like so:

    let [ted, jenni, athen] = names;
    console.log(ted, jenni, athen); // prints "Ted Jenni Athen"

Notice the array brackets around the variable declarations. The brackets inform ECMAScript that it is expecting an array on the right-hand side of the equals sign. If there are more elements in the array than in the list of declared variables, the remainder of the array is simply dropped. (The values remain in the array, of course—the array's values are copied to the variables, and the original array is left unaffected.) Should the array have less values than the list of declared variables, ECMAScript will fill any remaining variables with the value "undefined."

In many respects, the array form of destructuring assignment is simply syntactic sugar for an older form of the same operation:

    var ted = names[0];
    var jenni = names[1];
    var athen = names[2];

The destructuring version is simply shorter, and arguably more expressive.

Destructuring in objects

One of the most useful cases for a destructuring assignment would be to extract the values from a regular expression parse:

    var url = "http://www.newardassociates.com/#/speaking";
    var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
    var [, protocol, fullhost, fullpath] = parsedURL;
    console.log(protocol); // "http"

Similar kinds of destructuring can be done for objects, as well. Given a simple object, you could do something like this:

    let point = { x:2, y: 5 };
    let {x, y} = point;
    console.log(x, y); // prints 2, 5

In this particular case, the destructuring occurs on an object, and the variables are bound based on finding their name-equivalents in the object on the right-hand side. That is, even had we written the variables in reverse order, the value of x would still have received point.x and y would receive point.y:

    let point = { x:2, y: 5 };
    let {y, x} = point;
    console.log(x, y); // prints 2, 5

If for some reason you don't care to match the object's field names, you can rename the fields by using field-style syntax, with the left-hand side indicating the name to match, and the right-hand side indicating the actual declared variable name:

    let {y: pty, x: ptx} = point;
    console.log(ptx, pty); // prints 2, 5

This lets you exercise more control over variable naming when destructuring an object.

Destructuring can also occur more than one-level deep; for example, a rectangle is often represented as two points:

    let rect = { lowerLeft: { x:0, y:0 }, upperRight: { x:3, y:4} };

If you wanted to extract the four values of the rectangle, you could do so by pulling the two x/y pairs into four distinct variables, like so:

    let { lowerLeft: { x:llx, y: lly }, upperRight: { x:urx, y:ury } } 
        = rect;
    console.log(llx, lly, urx, ury); // prints "0 0 3 4"

In this case, "lowerLeft" and "upperRight" are not actual variables; they're placeholders indicating how their child fields should be bound against the respectively-named fields inside the object being destructured. Only the variables llx, lly, urx, and ury are actually introduced in this case.

That's enough about destructuring for now, but we're not done with it. In a future article you'll see how this new syntax works inside method parameters.

Conclusion

We've only just begun to investigate the changes that ECMAScript 6 has in store, yet already the language feels different from its predecessors. This latest round of specifications signals a new maturity, correcting for flaws that have pained developers for nigh on a decade now.

Some of the changes in ECMAScript 6 will be immediately welcome and quickly applicable—for example, it's pretty easy to start using let and const in place of var. Others will take more time to integrate, such as the backtick string syntax. Fortunately, all ECMAScript engines are still backward-compatible, so there's still time.

Speaking of time, we're out of it here. But turn to Part 2 when you're ready to explore function updates in ECMAScript 6.


Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=1038068
ArticleTitle=The busy JavaScript developer's guide to ECMAScript 6, Part 1: Variable declarations and more in the new JavaScript
publish-date=09292016