ES6 Features

5 minute read

Updated:

I took a refresher to learn about ES6 and the new language niceties (Source). Note that these are relative to my previous experience with Javascript, circa 2014. And also I’ve been writing python for the last 3 years.

Const, Let, and Var

var will hoist the variable, which moves it to the root of execution context. This is the enclosing function or global. This is like a header file in C, where all the declarations are interpreted first, before any code is ran. Many other languages don’t do this (implicitly), so it’s a gotcha.

const and let use block-scope instead.

Use const and let instead of var for declaration.

Because the scope due to hoisting is unexpectedly wide, there are many gotchas that arise:

  • Variable can be used before it’s “declared”. Instead of throwing error about referencing an undefined variable (not to be confused with javascript undefined value)
  • For loop variable will persist after loop completes (note: this exists in python)
  • Each iteration of a loop is the same execution context, so closures refer to the same variable reference. Block-scope makes separate variables for each iteration.

Array Functions

There are a full set of methods on Array now. Now more Array.forEach(myActualArray) shenanigans.

  1. forEach, to loop
  2. map, for transforming
  3. filter, for getting subset based on predicate
  4. find, for getting the first element of filter
  5. every, for checking predicate on all elements
  6. some, for checking that at least one element matches predicate
  7. reduce, to get single value after going through all items

Arrow Functions

Arrow functions do exactly what you expect if you come from other languages (Python, Java, C#), with respect to this handling. It correctly binds to the correct context, by default. And we all love sane defaults.

This was a major point of confusion for me, the need to use apply() or bind(). There’s probably a good reason, based on the language design. But I’m not looking to design language or interpreter, I just want my damn code to work. I’ll admit that I still don’t understand it but the point is I don’t need to. And my refusal to learn has zero detriment to 99% of my use cases of javascript functions.

Classes

Javascript is a protoypical language. In other languages, you declare classes and instantiate them. In Javascript, classes are objects and you clone them to “instantiate”.

ES6 has class syntax, which is simply syntactic sugar over this concept. While the mechanics are the same under the hood, this sugar is great for developers to keep in context. This is a good example of abstraction: developers don’t need to know about the nuances of prototyping vs. instantiation (for majority of cases) and it makes little difference on the outcome. If they need to leverage the features, it’s still accessible, albeit without syntactical sugar.

Object Literals

Object literals have some syntactic sugar too. If there’s only a value and no key, it’s implicitly the string name of the variable.

const user = 'bob';
const visit = {
    user;
};

console.log(visit.user);

Calculated properties are also available. This just means the key name is dynamic.

const visit = {
    ['foo' + 'bar']: 1,
}

Note: I realized this contrived example is bad because string concatenation of literals probably gets lexically parsed away to a single token. But the idea is there.

Template Strings

FINALLY. When I started using javascript and wanted to format strings, I looked for a string.format() or a printf function. Only to discover that people were hand-rolling their own adhoc functions. Literally everyone was copy-pasting the same function. I could use a library but isn’t that what a standard library is for? Shit that I need every day and are probably going ot import into my project by default.

Template strings make the most mundane, yet tedious, task no longer a valid gripe of mine. In fact, it skips an evolutionary step altogether and is a language construct, not simply a function.

const word = 'cool';
console.log(`I can print {word} things.`);

These operate the same as f-strings in python 3. They evaluate and substitute expressions. The same best practices would pertain here: don’t embed lots of logic, only minimal presentation-related logic.

Default Arguments

Huh, I didn’t know Javascript didn’t allow for this before. I wonder if it was trying to be like Java and encourage function overloading instead.

Parameter defaults are just like in Python.

const substring = (string, startIndex = 0, length = -1) => {
};

Rest and Spread

Rest operator is how to handle variadic parameters. In python, this is*args for positional params and *kwargs for keyword params.

const substring = (string, ...others) => {
};

The easy to use and reads syntax makes it feasible to consider writing forwards compatible functionality. I’m not entirely sure how useful it will be in real-world.

Spread operator allows for iterable object to expand into positional params (reverse of rest operator).

const param = [1, 2, 3];
substring('test string', ...param) == substring('test string', 1, 2, 3);

Destructuring

This is really cool. It’s similar to tuple-unpacking in Python but more powerful.

const printFirstName = ({firstName}) => {
    console.log(firstName);
};

const user = { firstName: 'John', lastName: 'Nguyen'};
printFirstName(user);

The function requires an object for param and only gets the target property from it. This reduces a lot of boilerplate noise that arises from continually dereferencing properties.

You can even use the rest operator to pluck out single attributes! This is more commonly useful in JSX to pass remainder of props to child.

const printNotFirstName = ({firstName, ...rest}) => {
    console.log(rest);
};

const user = { firstName: 'John', lastName: 'Nguyen'};
printFirstName(user);

It also works for arrays, this is more traditional tuple-unpacking.

const printFullName = ([firstName, , lastName]) => {
    console.log(`${firstName} ${lastName}`);
};

printFullName(['John', 'Something', 'Nguyen', 'Jr.', 'the First']);

Promises

I’m still trying to wrap my head around promises. It’s a syntax style for writing asynchronous code for event-loop processing. With event-driven programming, there tends to be a lot of callbacks, which in turn have callbacks. To avoid all the nesting, promises are a form of “sugar” using words “then”, “reject”, “resolve”, “catch”, etc.

const user = callService('get_user').then(response => {
    return response.userId;
}).catch(response => {
    if (response.timeout) {
        // Service timeout
        throw TimeoutError();
    } else {
        // User doesn't exist
        return "Guest";
    }
}).then( user => {
    logUser(user)
});

// Old way
const user = callService('get_user', response => {
    let user;
    if (response.okay) {
        user = response.userId;
    } else if (response.Timeout) {
        throw TimeoutError();
    } else {
        user = 'Guest';
    }

    logUser(user);
    return user;
});
    return response.userId;
}).catch(response => {
    if (response.timeout)
        // User doesn't exist
        return "Guest";
    }
})

I haven’t illustrated how unreadable it can get but hopefully this is enough to get your mind started thinking about it.